feat(installer): 便携中文安装包「年糕历险记-Setup-v0.1.0.zip」——安装.bat 中文交互+桌面快捷方式;修复 ps1 BOM 编码

This commit is contained in:
NianGao Dev
2026-08-15 23:59:24 +08:00
parent 610f234bb4
commit 4755fd88fc
7 changed files with 171 additions and 3 deletions

View File

@@ -26,6 +26,8 @@ import (
"mc/internal/item"
"mc/internal/logx"
"mc/internal/mesh"
"mc/internal/netclient"
"mc/internal/netproto"
"mc/internal/physics"
"mc/internal/recipe"
"mc/internal/render"
@@ -51,6 +53,10 @@ type clientApp struct {
titleScreen *ui.TitleScreen
pauseScreen *ui.PauseScreen
// 联机(多人同步.md §2:无连接时为单机)
net *netclient.Client
log *logx.Logger
meshBuilder *mesh.Builder
mu sync.Mutex
@@ -77,6 +83,15 @@ func (a *clientApp) Tick(dt float64) {
case "play":
a.screen = 1
a.rend.SetCursorMode(true)
case "multi":
// 联机:连接本机服务器(多人同步.md §2)
if err := dialServer(a, "127.0.0.1:25565", "玩家"); err != nil {
a.logf("连接服务器失败: %v", err)
} else {
a.screen = 1
a.rend.SetCursorMode(true)
a.logf("已连接服务器")
}
case "quit":
a.rend.Window().SetShouldClose(true)
}
@@ -112,9 +127,19 @@ func (a *clientApp) Tick(dt float64) {
a.in.apply(a.sess)
a.sess.Tick(dt)
// 联机:上报移动(20Hz,多人同步.md §3)
if a.net != nil {
p := a.sess.Player
a.net.SendMove(netproto.PlayerMove{
X: float32(p.Pos[0]), Y: float32(p.Pos[1]), Z: float32(p.Pos[2]),
Yaw: float32(a.sess.Cam.Yaw), Pitch: float32(a.sess.Cam.Pitch),
OnGround: p.OnGround,
})
}
// 定时存档:30s(存档与持久化.md §2)
a.tickCnt++
if a.tickCnt%(20*30) == 0 {
if a.tickCnt%(20*30) == 0 && a.net == nil {
if err := save.SaveWorld(a.sess.World, "worlds", "NewWorld", 123456789, int64(a.tickCnt)); err != nil {
// 日志由调用方输出(避免每次 tick 刷屏)
_ = err
@@ -178,6 +203,13 @@ func (a *clientApp) Render() {
// ShouldClose 窗口关闭判定。
func (a *clientApp) ShouldClose() bool { return a.rend.ShouldClose() }
// logf 客户端日志(联机回调使用)。
func (a *clientApp) logf(format string, args ...any) {
if a.log != nil {
a.log.Infof(format, args...)
}
}
func main() {
flag.Parse()
@@ -341,6 +373,7 @@ func main() {
hud: hud,
titleScreen: titleScreen,
pauseScreen: pauseScreen,
log: log,
meshBuilder: mesh.NewBuilder(reg, uvFn),
winW: cfg.Client.WindowWidth,
winH: cfg.Client.WindowHeight,

View File

@@ -0,0 +1,52 @@
//go:build gl
// 客户端联机:netclient 接入(多人同步.md §2 权威链路客户端侧)。
package main
import (
"mc/internal/block"
"mc/internal/netclient"
"mc/internal/netproto"
"mc/internal/save"
)
// mpHandler 联机回调:区块安装与方块变更应用(多人同步.md §4–§5)。
type mpHandler struct {
app *clientApp
}
// OnLogin 登录结果。
func (h *mpHandler) OnLogin(code uint8) {
if code != 0 {
h.app.logf("服务器拒绝登录(码 %d)", code)
h.app.net.Close()
h.app.net = nil
h.app.screen = 0
}
}
// OnChunk 收到区块:反序列化并安装到世界(多人同步.md §4)。
func (h *mpHandler) OnChunk(data []byte) {
c, err := save.DecodeChunk(data)
if err != nil {
h.app.logf("区块解码失败: %v", err)
return
}
h.app.sess.World.InstallChunk(c)
}
// OnBlockChange 方块变更(服务器权威广播,多人同步.md §5)。
func (h *mpHandler) OnBlockChange(m netproto.BlockChange) {
h.app.sess.World.SetBlock(m.X, m.Y, m.Z, block.NewState(m.BlockID, m.Meta))
}
// dialServer 连接服务器(多人游戏按钮,多人同步.md §2)。
func dialServer(a *clientApp, addr string, name string) error {
h := &mpHandler{app: a}
nc, err := netclient.Dial(addr, netproto.Handshake{ProtocolVersion: 1, ClientID: name}, h)
if err != nil {
return err
}
a.net = nc
return nil
}

View File

@@ -52,7 +52,7 @@ func buildScreens(r *ui.Renderer, lang *assets.Lang) (*ui.TitleScreen, *ui.Pause
title.TitleH = titleH
title.Buttons = []ui.Button{
btn("menu.singleplayer", "单人游戏", "play", 300, 1280, 720),
btn("menu.multiplayer", "多人游戏", "", 350, 1280, 720),
btn("menu.multiplayer", "多人游戏", "multi", 350, 1280, 720),
btn("menu.quit", "退出游戏", "quit", 400, 1280, 720),
}
_ = titleTex