initial commit

This commit is contained in:
AkmalFairuz
2024-12-31 20:57:21 +07:00
commit 0de5415f05
72 changed files with 20340 additions and 0 deletions

136
mapping/block.go Normal file
View File

@@ -0,0 +1,136 @@
package mapping
import (
"bytes"
"github.com/akmalfairuz/legacy-version/internal"
"sort"
"github.com/df-mc/worldupgrader/blockupgrader"
"github.com/sandertv/gophertunnel/minecraft/nbt"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/segmentio/fasthash/fnv1"
)
type Block interface {
// StateToRuntimeID converts a block state to a runtime ID.
StateToRuntimeID(blockupgrader.BlockState) (uint32, bool)
// RuntimeIDToState converts a runtime ID to a name and its state properties.
RuntimeIDToState(uint32) (blockupgrader.BlockState, bool)
// DowngradeBlockActorData downgrades the input sub chunk to a legacy block actor.
DowngradeBlockActorData(map[string]any)
// UpgradeBlockActorData upgrades the input sub chunk to the latest block actor.
UpgradeBlockActorData(map[string]any)
// Adjust adjusts the latest mappings to account for custom states.
Adjust([]protocol.BlockEntry)
Air() uint32
}
type DefaultBlockMapping struct {
// states holds a list of all possible vanilla block states.
states []blockupgrader.BlockState
// stateRuntimeIDs holds a map for looking up the runtime ID of a block by the stateHash it produces.
stateRuntimeIDs map[internal.StateHash]uint32
// runtimeIDToState holds a map for looking up the blockState of a block by its runtime ID.
runtimeIDToState map[uint32]blockupgrader.BlockState
upgrader, downgrader func(map[string]any) map[string]any
// airRID is the runtime ID of the air block in the latest version of the game.
airRID uint32
}
func NewBlockMapping(raw []byte) *DefaultBlockMapping {
dec := nbt.NewDecoder(bytes.NewBuffer(raw))
var states []blockupgrader.BlockState
stateRuntimeIDs := make(map[internal.StateHash]uint32)
runtimeIDToState := make(map[uint32]blockupgrader.BlockState)
var airRID *uint32
var s blockupgrader.BlockState
for {
if err := dec.Decode(&s); err != nil {
break
}
rid := uint32(len(states))
states = append(states, s)
if s.Name == "minecraft:air" {
airRID = &rid
}
stateRuntimeIDs[internal.HashState(blockupgrader.Upgrade(s))] = rid
runtimeIDToState[rid] = s
}
if airRID == nil {
panic("couldn't find air")
}
return &DefaultBlockMapping{
states: states,
stateRuntimeIDs: stateRuntimeIDs,
runtimeIDToState: runtimeIDToState,
airRID: *airRID,
}
}
func (m *DefaultBlockMapping) WithBlockActorRemapper(downgrader, upgrader func(map[string]any) map[string]any) *DefaultBlockMapping {
m.downgrader = downgrader
m.upgrader = upgrader
return m
}
func (m *DefaultBlockMapping) StateToRuntimeID(state blockupgrader.BlockState) (uint32, bool) {
rid, ok := m.stateRuntimeIDs[internal.HashState(blockupgrader.Upgrade(state))]
return rid, ok
}
func (m *DefaultBlockMapping) RuntimeIDToState(runtimeId uint32) (blockupgrader.BlockState, bool) {
state, found := m.runtimeIDToState[runtimeId]
return state, found
}
func (m *DefaultBlockMapping) DowngradeBlockActorData(actorData map[string]any) {
if m.downgrader != nil {
m.downgrader(actorData)
}
}
func (m *DefaultBlockMapping) UpgradeBlockActorData(actorData map[string]any) {
if m.upgrader != nil {
m.upgrader(actorData)
}
}
func (m *DefaultBlockMapping) Adjust(entries []protocol.BlockEntry) {
if len(entries) == 0 {
return
}
customStates := convert(entries)
var newStates []blockupgrader.BlockState
for _, state := range customStates {
if _, ok := m.StateToRuntimeID(state); !ok {
newStates = append(newStates, state)
}
}
if len(newStates) == 0 {
return
}
adjustedStates := append(m.states, customStates...)
sort.SliceStable(adjustedStates, func(i, j int) bool {
stateOne, stateTwo := adjustedStates[i], adjustedStates[j]
return stateOne.Name != stateTwo.Name && fnv1.HashString64(stateOne.Name) < fnv1.HashString64(stateTwo.Name)
})
m.stateRuntimeIDs = make(map[internal.StateHash]uint32, len(adjustedStates))
m.runtimeIDToState = make(map[uint32]blockupgrader.BlockState, len(adjustedStates))
for rid, state := range adjustedStates {
m.stateRuntimeIDs[internal.HashState(blockupgrader.Upgrade(state))] = uint32(rid)
m.runtimeIDToState[uint32(rid)] = state
}
}
func (m *DefaultBlockMapping) Air() uint32 {
return m.airRID
}

84
mapping/custom_blocks.go Normal file
View File

@@ -0,0 +1,84 @@
package mapping
import (
"github.com/akmalfairuz/legacy-version/internal"
"github.com/df-mc/worldupgrader/blockupgrader"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"golang.org/x/exp/maps"
)
func convert(entries []protocol.BlockEntry) (states []blockupgrader.BlockState) {
for _, entry := range entries {
propertiesMap := map[string][]any{}
if props := jsonCheck[[]any](entry.Properties, "properties"); props != nil {
for _, prop := range *props {
prop := prop.(map[string]any)
name := jsonCheck[string](prop, "name")
enum := jsonCheck[[]any](prop, "enum")
if enum == nil {
int32Enum := jsonCheck[[]int32](prop, "enum")
if int32Enum != nil {
enum = &[]any{}
for _, i := range *int32Enum {
*enum = append(*enum, i)
}
}
}
if name == nil || enum == nil {
panic("could not find field `name` and `enum`")
}
propertiesMap[*name] = *enum
}
}
combinations := make([]map[string]any, 0)
generateCombinationsRecursively(propertiesMap, internal.NewIterator(maps.Keys(propertiesMap)), map[string]any{}, &combinations)
if len(combinations) == 0 {
combinations = append(combinations, map[string]any{})
}
for _, combination := range combinations {
blockState := blockupgrader.BlockState{
Name: entry.Name,
Properties: combination,
}
states = append(states, blockState)
}
}
return
}
func generateCombinationsRecursively[K comparable, V any](all map[K][]V, iterator *internal.Iterator[K], current map[K]V, output *[]map[K]V) {
if !iterator.HasNext() {
entry := map[K]V{}
for k, v := range current {
entry[k] = v
}
out := append(*output, entry)
*output = out
} else {
key := iterator.Next()
set := all[key]
for _, value := range set {
current[key] = value
generateCombinationsRecursively(all, iterator, current, output)
delete(current, key)
}
iterator.Previous()
}
}
func jsonCheck[T any](json map[string]any, field string) *T {
fieldValue, found := json[field]
if !found {
return nil
}
castedValue, ok := fieldValue.(T)
if !ok {
return nil
}
return &castedValue
}

109
mapping/item.go Normal file
View File

@@ -0,0 +1,109 @@
package mapping
import (
"encoding/json"
"github.com/sandertv/gophertunnel/minecraft/nbt"
"sync"
)
type Item interface {
// ItemRuntimeIDToName converts an item runtime ID to a string ID.
ItemRuntimeIDToName(int32) (string, bool)
// ItemNameToRuntimeID converts a string ID to an item runtime ID.
ItemNameToRuntimeID(string) (int32, bool)
RegisterEntry(string) int32
Air() int32
ItemVersion() uint16
}
type DefaultItemMapping struct {
mu sync.Mutex
// itemRuntimeIDsToNames holds a map to translate item runtime IDs to string IDs.
itemRuntimeIDsToNames map[int32]string
// itemNamesToRuntimeIDs holds a map to translate item string IDs to runtime IDs.
itemNamesToRuntimeIDs map[string]int32
airRID int32
itemVersion uint16
}
func NewItemMapping(itemRuntimeIDData []byte, requiredItemList []byte, itemVersion uint16, direct bool) *DefaultItemMapping {
itemRuntimeIDsToNames := make(map[int32]string)
itemNamesToRuntimeIDs := make(map[string]int32)
var airRID *int32
if direct {
var items map[string]int32
if err := nbt.Unmarshal(itemRuntimeIDData, &items); err != nil {
panic(err)
}
for name, rid := range items {
if name == "minecraft:air" {
airRID = &rid
}
itemNamesToRuntimeIDs[name] = rid
itemRuntimeIDsToNames[rid] = name
}
} else {
var m map[string]struct {
RuntimeID int16 `json:"runtime_id"`
ComponentBased bool `json:"component_based"`
}
if err := json.Unmarshal(requiredItemList, &m); err != nil {
panic(err)
}
for name, data := range m {
rid := int32(data.RuntimeID)
if name == "minecraft:air" {
airRID = &rid
}
itemNamesToRuntimeIDs[name] = rid
itemRuntimeIDsToNames[rid] = name
}
}
if airRID == nil {
panic("couldn't find air")
}
return &DefaultItemMapping{itemRuntimeIDsToNames: itemRuntimeIDsToNames, itemNamesToRuntimeIDs: itemNamesToRuntimeIDs, itemVersion: itemVersion}
}
func (m *DefaultItemMapping) ItemRuntimeIDToName(runtimeID int32) (name string, found bool) {
defer m.mu.Unlock()
m.mu.Lock()
name, ok := m.itemRuntimeIDsToNames[runtimeID]
return name, ok
}
func (m *DefaultItemMapping) ItemNameToRuntimeID(name string) (runtimeID int32, found bool) {
defer m.mu.Unlock()
m.mu.Lock()
rid, ok := m.itemNamesToRuntimeIDs[name]
return rid, ok
}
func (m *DefaultItemMapping) RegisterEntry(name string) int32 {
defer m.mu.Unlock()
m.mu.Lock()
if rid, ok := m.itemNamesToRuntimeIDs[name]; ok {
return rid
}
nextRID := int32(len(m.itemRuntimeIDsToNames))
m.itemNamesToRuntimeIDs[name] = nextRID
m.itemRuntimeIDsToNames[nextRID] = name
return nextRID
}
func (m *DefaultItemMapping) Air() int32 {
defer m.mu.Unlock()
m.mu.Lock()
return m.airRID
}
func (m *DefaultItemMapping) ItemVersion() uint16 {
defer m.mu.Unlock()
m.mu.Lock()
return m.itemVersion
}