Initial commit: RegionIO Minecraft server core (26.1.2/protocol 775)
Vanilla-faithful overworld generator (final_density + multi-noise biomes), full connection lifecycle (status/login/configuration/play), chunk streaming, creative block editing, and the protocol/nbt/registry infrastructure.
This commit is contained in:
commit
a7bb9496ae
146 changed files with 217621 additions and 0 deletions
220
internal/network/configuration.go
Normal file
220
internal/network/configuration.go
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"regionio/internal/protocol"
|
||||
"regionio/internal/registry"
|
||||
)
|
||||
|
||||
// ClientSettings holds the subset of client_information we currently track.
|
||||
type ClientSettings struct {
|
||||
Locale string
|
||||
ViewDistance int8
|
||||
ChatMode int32
|
||||
MainHand int32
|
||||
}
|
||||
|
||||
// beginConfiguration is called on entering the configuration phase. It mirrors
|
||||
// the vanilla opening sequence: server brand, enabled feature flags, then the
|
||||
// known-packs negotiation. The client's known-packs reply triggers the registry
|
||||
// data (see handleKnownPacks).
|
||||
func (h *handler) beginConfiguration() error {
|
||||
if err := h.sendBrand(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.sendEnabledFeatures(); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.sendKnownPacks()
|
||||
}
|
||||
|
||||
// sendBrand reports the server brand on the minecraft:brand plugin channel.
|
||||
func (h *handler) sendBrand() error {
|
||||
w := protocol.NewWriter(32)
|
||||
w.String("minecraft:brand").String("RegionIO")
|
||||
return h.conn.SendWriter(protocol.ConfigCustomPayloadCB, w)
|
||||
}
|
||||
|
||||
// sendEnabledFeatures enables the vanilla feature flag set. The client requires
|
||||
// this so that vanilla content (blocks, items, registries) is active.
|
||||
func (h *handler) sendEnabledFeatures() error {
|
||||
w := protocol.NewWriter(24)
|
||||
w.VarInt(1)
|
||||
w.String("minecraft:vanilla")
|
||||
return h.conn.SendWriter(protocol.ConfigUpdateEnabledFeatures, w)
|
||||
}
|
||||
|
||||
// handleConfiguration drives the configuration phase:
|
||||
//
|
||||
// S→C select_known_packs (on entry)
|
||||
// C→S client_information → record settings
|
||||
// C→S custom_payload (brand) → log the client brand
|
||||
// C→S select_known_packs → send registry data, then finish_configuration
|
||||
// C→S finish_configuration → switch to the Play phase
|
||||
func (h *handler) handleConfiguration(pkt protocol.Packet) error {
|
||||
switch pkt.ID {
|
||||
case protocol.ConfigClientInformation:
|
||||
return h.handleClientInformation(pkt)
|
||||
|
||||
case protocol.ConfigCustomPayload:
|
||||
return h.handleConfigCustomPayload(pkt)
|
||||
|
||||
case protocol.ConfigKnownPacksServer:
|
||||
return h.handleKnownPacks(pkt)
|
||||
|
||||
case protocol.ConfigKeepAliveServer, protocol.ConfigPong,
|
||||
protocol.ConfigResourcePackResp, protocol.ConfigCookieResponse:
|
||||
h.log.Debug("configuration packet ignored", "id", pkt.ID)
|
||||
return nil
|
||||
|
||||
case protocol.ConfigFinishServerbound:
|
||||
h.conn.SetState(protocol.StatePlay)
|
||||
h.log.Info("entered play phase", "name", h.conn.Profile.Name)
|
||||
return h.beginPlay()
|
||||
|
||||
default:
|
||||
h.log.Debug("unknown configuration packet", "id", pkt.ID)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// handleClientInformation records the client's settings (no longer the trigger
|
||||
// to finish configuration; that is now driven by known-packs negotiation).
|
||||
func (h *handler) handleClientInformation(pkt protocol.Packet) error {
|
||||
r := pkt.Body()
|
||||
locale, err := r.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vd, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chatMode, err := r.VarInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := r.Bool(); err != nil { // chat colors
|
||||
return err
|
||||
}
|
||||
if _, err := r.ReadByte(); err != nil { // displayed skin parts
|
||||
return err
|
||||
}
|
||||
mainHand, err := r.VarInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.log.Info("client information",
|
||||
"locale", locale, "view_distance", int8(vd),
|
||||
"chat_mode", chatMode, "main_hand", mainHand)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleConfigCustomPayload logs the client brand and ignores other channels.
|
||||
func (h *handler) handleConfigCustomPayload(pkt protocol.Packet) error {
|
||||
r := pkt.Body()
|
||||
channel, err := r.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channel == "minecraft:brand" {
|
||||
brand, err := r.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.log.Info("client brand", "brand", brand)
|
||||
} else {
|
||||
h.log.Debug("plugin message", "channel", channel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleKnownPacks reads the client's known packs and, once we know what it
|
||||
// already has, sends the registry data followed by finish_configuration.
|
||||
func (h *handler) handleKnownPacks(pkt protocol.Packet) error {
|
||||
r := pkt.Body()
|
||||
count, err := r.VarInt()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count < 0 || count > 1024 {
|
||||
return errors.New("implausible known-pack count")
|
||||
}
|
||||
hasCore := false
|
||||
for i := int32(0); i < count; i++ {
|
||||
ns, err := r.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := r.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ver, err := r.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ns == registry.CorePack.Namespace && id == registry.CorePack.ID &&
|
||||
ver == registry.CorePack.Version {
|
||||
hasCore = true
|
||||
}
|
||||
h.log.Debug("client known pack", "namespace", ns, "id", id, "version", ver)
|
||||
}
|
||||
if !hasCore {
|
||||
// Without the matching pack the client cannot fill in registry data
|
||||
// from its built-in copy; sending has_data=false would desync it.
|
||||
h.log.Warn("client lacks matching core pack; registry data may desync",
|
||||
"want", registry.CorePack.Version)
|
||||
}
|
||||
|
||||
if err := h.sendRegistries(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.sendUpdateTags(); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.sendFinishConfiguration()
|
||||
}
|
||||
|
||||
// sendKnownPacks advertises the vanilla core pack to the client.
|
||||
func (h *handler) sendKnownPacks() error {
|
||||
p := registry.CorePack
|
||||
w := protocol.NewWriter(32)
|
||||
w.VarInt(1)
|
||||
w.String(p.Namespace).String(p.ID).String(p.Version)
|
||||
return h.conn.SendWriter(protocol.ConfigKnownPacksCB, w)
|
||||
}
|
||||
|
||||
// sendRegistries sends one registry_data packet per synchronized registry. Each
|
||||
// entry is sent with has_data=false; the client supplies the contents from its
|
||||
// matching known pack.
|
||||
func (h *handler) sendRegistries() error {
|
||||
for _, reg := range registry.Synced() {
|
||||
w := protocol.NewWriter(64 + len(reg.Entries)*24)
|
||||
w.String(reg.Name)
|
||||
w.VarInt(int32(len(reg.Entries)))
|
||||
for _, entry := range reg.Entries {
|
||||
w.String(entry)
|
||||
w.Bool(false) // has_data: client uses its own copy
|
||||
}
|
||||
if err := h.conn.SendWriter(protocol.ConfigRegistryData, w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
h.log.Debug("sent registry data", "registries", len(registry.Synced()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendUpdateTags sends the captured vanilla tag set. Tags map registry entries
|
||||
// (by numeric index) into named groups the client and gameplay rely on.
|
||||
func (h *handler) sendUpdateTags() error {
|
||||
return h.conn.Send(protocol.ConfigUpdateTags, registry.Tags())
|
||||
}
|
||||
|
||||
// sendFinishConfiguration signals configuration is complete; the client replies
|
||||
// with its own finish_configuration to enter Play.
|
||||
func (h *handler) sendFinishConfiguration() error {
|
||||
return h.conn.Send(protocol.ConfigFinishClientbound, nil)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue