build: GL 渲染层与客户端挂 gl 构建标签(无 C 工具链也可构建测试);构建脚本自动定位 zig/gcc
This commit is contained in:
215
cmd/client/client_gl.go
Normal file
215
cmd/client/client_gl.go
Normal file
@@ -0,0 +1,215 @@
|
||||
//go:build gl
|
||||
|
||||
// 年糕历险记 —— 客户端入口(GL 构建)。
|
||||
//
|
||||
// 装配顺序(架构.md §3.1 启动流程):配置 → 日志 → 内容包/图集 → 注册表 → 世界
|
||||
// → 游戏会话(玩家/背包/相机)→ 渲染器 → 主循环(tick 20Hz + render)。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"mc/internal/assets"
|
||||
"mc/internal/atlas"
|
||||
"mc/internal/block"
|
||||
"mc/internal/camera"
|
||||
"mc/internal/config"
|
||||
"mc/internal/engine"
|
||||
"mc/internal/game"
|
||||
"mc/internal/inventory"
|
||||
"mc/internal/item"
|
||||
"mc/internal/logx"
|
||||
"mc/internal/mesh"
|
||||
"mc/internal/physics"
|
||||
"mc/internal/recipe"
|
||||
"mc/internal/render"
|
||||
"mc/internal/world"
|
||||
"mc/internal/worldgen"
|
||||
)
|
||||
|
||||
var devMode = flag.Bool("dev", devDefault, "开发模式:直接读取 assets/(默认随构建 tag)")
|
||||
|
||||
// clientApp 客户端应用:把会话与渲染粘合起来(engine.Ticker + engine.Renderer)。
|
||||
type clientApp struct {
|
||||
sess *game.Session
|
||||
rend *render.Renderer
|
||||
reg *block.Registry
|
||||
in *inputState
|
||||
|
||||
meshBuilder *mesh.Builder
|
||||
|
||||
mu sync.Mutex
|
||||
pendingMesh []*worldMeshJob // tick 构建 → 渲染线程上传
|
||||
|
||||
winW, winH int
|
||||
lastTime time.Time
|
||||
}
|
||||
|
||||
// worldMeshJob 待上传网格。
|
||||
type worldMeshJob struct {
|
||||
cx, cz int32
|
||||
m *mesh.ChunkMesh
|
||||
}
|
||||
|
||||
// Tick 每 tick(20Hz):输入 → 玩家 → 会话 → 网格构建(渲染.md §3.3 dirty 重建)。
|
||||
func (a *clientApp) Tick(dt float64) {
|
||||
// 输入 → 玩家速度(UI与输入.md §6 动作映射)
|
||||
a.in.apply(a.sess)
|
||||
a.sess.Tick(dt)
|
||||
|
||||
// 脏区块网格重建(CPU 侧,预算内)
|
||||
start := time.Now()
|
||||
for _, c := range a.sess.World.ActiveChunks() {
|
||||
if time.Since(start) > 6*time.Millisecond {
|
||||
break // 帧预算(区块管理.md §4.3.2)
|
||||
}
|
||||
if !c.Dirty.Load() {
|
||||
continue
|
||||
}
|
||||
m := a.meshBuilder.Build(c, func(x, y, z int32) block.State {
|
||||
return a.sess.World.Block(x, y, z)
|
||||
})
|
||||
a.mu.Lock()
|
||||
a.pendingMesh = append(a.pendingMesh, &worldMeshJob{cx: c.CX, cz: c.CZ, m: m})
|
||||
a.mu.Unlock()
|
||||
c.Dirty.Store(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Render 每帧渲染(渲染线程):上传待传网格 → 视图矩阵 → 三 pass 绘制。
|
||||
func (a *clientApp) Render() {
|
||||
// 上传 tick 构建好的网格(GL 调用必须在渲染线程)
|
||||
a.mu.Lock()
|
||||
pending := a.pendingMesh
|
||||
a.pendingMesh = nil
|
||||
a.mu.Unlock()
|
||||
for _, j := range pending {
|
||||
a.rend.UploadChunk(j.cx, j.cz, j.m)
|
||||
}
|
||||
|
||||
// 视图投影
|
||||
cam := a.sess.Cam
|
||||
aspect := float64(a.winW) / float64(a.winH)
|
||||
a.rend.SetViewProj(cam.View(), cam.Projection(aspect, 0.05, 1024))
|
||||
|
||||
// 清屏 + 绘制(视锥剔除简化为全量绘制,后置 LOD)
|
||||
a.rend.BeginFrame()
|
||||
chunks := a.sess.World.ActiveChunks()
|
||||
a.rend.DrawChunks(func(i int) (int32, int32) { return chunks[i].CX, chunks[i].CZ }, len(chunks))
|
||||
a.rend.EndFrame()
|
||||
}
|
||||
|
||||
// ShouldClose 窗口关闭判定。
|
||||
func (a *clientApp) ShouldClose() bool { return a.rend.ShouldClose() }
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
log, err := logx.New("logs/client.log", logx.LevelInfo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfg, err := config.Load("config/client.yaml", config.DefaultClient())
|
||||
if err != nil {
|
||||
log.Warnf("配置加载失败,使用默认值:%v", err)
|
||||
}
|
||||
log.Infof("%s 客户端启动:%dx%d,语言 %s,视距 %d", cfg.Client.WindowTitle, cfg.Client.WindowWidth, cfg.Client.WindowHeight, cfg.Client.Language, cfg.Client.RenderDistance)
|
||||
|
||||
// 1) 内容包与图集(设计.md §1.3:扫描 mods/ + resourcepacks/)
|
||||
am := assets.New()
|
||||
if err := am.Load("assets", "mods", "resourcepacks"); err != nil {
|
||||
log.Warnf("内容包加载失败:%v", err)
|
||||
}
|
||||
log.Infof("已加载 %d 个内容包", len(am.Packs()))
|
||||
|
||||
keys, err := am.ExpandAtlas("blocks")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
atlasImg, uvRects, err := atlas.Build(am.ResolveTexture, keys, 16)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Infof("blocks 图集:%d 纹理,%dx%d", len(uvRects), atlasImg.Bounds().Dx(), atlasImg.Bounds().Dy())
|
||||
|
||||
// UV 函数(渲染.md §4:inset 防渗色)
|
||||
uvFn := func(texKey string) (u0, v0, u1, v1 float32, ok bool) {
|
||||
rect, found := uvRects[texKey]
|
||||
if !found {
|
||||
return 0, 0, 1, 1, false
|
||||
}
|
||||
W, H := float32(atlasImg.Bounds().Dx()), float32(atlasImg.Bounds().Dy())
|
||||
const inset = 0.5
|
||||
u0 = (float32(rect.Min.X) + inset) / W
|
||||
v0 = (float32(rect.Min.Y) + inset) / H
|
||||
u1 = (float32(rect.Max.X) - inset) / W
|
||||
v1 = (float32(rect.Max.Y) - inset) / H
|
||||
return u0, v0, u1, v1, true
|
||||
}
|
||||
|
||||
// 2) 注册表(设计.md §6.1/§6.2 数据驱动)
|
||||
reg, err := block.Load("assets/config/blocks.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
items, err := item.Load("assets/config/items.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
recipes, err := recipe.Load("assets/config/recipes.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 3) 世界与生成器(区块管理.md §4 异步流水线)
|
||||
gen, err := worldgen.New(reg, 123456789, 0.005, 0.01, 0.1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
w, err := world.New(reg, gen, cfg.Client.RenderDistance)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
// 4) 游戏会话(玩家先高空下落,区块加载后落地)
|
||||
p := physics.NewPlayer(8.5, 100, 8.5)
|
||||
cam := camera.New(p.Eye(), 0, 0, cfg.Client.FOV)
|
||||
inv := inventory.New(items)
|
||||
sess := game.NewSession(w, reg, items, recipes, p, inv, cam)
|
||||
|
||||
// 5) 渲染器(GL 线程亲和在其内部锁定)
|
||||
rend, err := render.New(cfg.Client.WindowTitle, cfg.Client.WindowWidth, cfg.Client.WindowHeight)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rend.Shutdown()
|
||||
rend.SetAtlas(atlasImg)
|
||||
rend.SetCursorMode(true)
|
||||
|
||||
// 6) 输入(鼠标捕获 + 键鼠回调)
|
||||
in := newInputState(rend.Window())
|
||||
|
||||
app := &clientApp{
|
||||
sess: sess,
|
||||
rend: rend,
|
||||
reg: reg,
|
||||
in: in,
|
||||
meshBuilder: mesh.NewBuilder(reg, uvFn),
|
||||
winW: cfg.Client.WindowWidth,
|
||||
winH: cfg.Client.WindowHeight,
|
||||
lastTime: time.Now(),
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer stop()
|
||||
|
||||
log.Infof("进入游戏主循环")
|
||||
engine.Run(ctx, app, app)
|
||||
log.Infof("客户端已退出")
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build gl
|
||||
|
||||
// 客户端输入处理:键鼠状态采集(UI与输入.md §6)。
|
||||
//
|
||||
// 设计:回调只更新状态,游戏逻辑在 tick 里消费状态(主循环解耦)。
|
||||
|
||||
@@ -1,217 +1,22 @@
|
||||
// 年糕历险记 —— 客户端入口。
|
||||
//go:build !gl
|
||||
|
||||
// 年糕历险记 —— 客户端入口(无 GL 构建:提示需要 C 工具链)。
|
||||
//
|
||||
// 装配顺序(架构.md §3.1 启动流程):配置 → 日志 → 内容包/图集 → 注册表 → 世界
|
||||
// → 游戏会话(玩家/背包/相机)→ 渲染器 → 主循环(tick 20Hz + render)。
|
||||
// 渲染层(internal/render)需要 CGO(GLFW/OpenGL),未启用 gl 构建标签时
|
||||
// 客户端只打印提示。完整客户端见 client_gl.go(-tags gl)。
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"mc/internal/assets"
|
||||
"mc/internal/atlas"
|
||||
"mc/internal/block"
|
||||
"mc/internal/camera"
|
||||
"mc/internal/config"
|
||||
"mc/internal/engine"
|
||||
"mc/internal/game"
|
||||
"mc/internal/inventory"
|
||||
"mc/internal/item"
|
||||
"mc/internal/logx"
|
||||
"mc/internal/mesh"
|
||||
"mc/internal/physics"
|
||||
"mc/internal/recipe"
|
||||
"mc/internal/render"
|
||||
"mc/internal/world"
|
||||
"mc/internal/worldgen"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var devMode = flag.Bool("dev", devDefault, "开发模式:直接读取 assets/(默认随构建 tag)")
|
||||
|
||||
// clientApp 客户端应用:把会话与渲染粘合起来(engine.Ticker + engine.Renderer)。
|
||||
type clientApp struct {
|
||||
sess *game.Session
|
||||
rend *render.Renderer
|
||||
reg *block.Registry
|
||||
in *inputState
|
||||
|
||||
meshBuilder *mesh.Builder
|
||||
|
||||
mu sync.Mutex
|
||||
pendingMesh []*worldMeshJob // tick 构建 → 渲染线程上传
|
||||
|
||||
winW, winH int
|
||||
lastTime time.Time
|
||||
}
|
||||
|
||||
// worldMeshJob 待上传网格。
|
||||
type worldMeshJob struct {
|
||||
cx, cz int32
|
||||
m *mesh.ChunkMesh
|
||||
}
|
||||
|
||||
// Tick 每 tick(20Hz):输入 → 玩家 → 会话 → 网格构建(渲染.md §3.3 dirty 重建)。
|
||||
func (a *clientApp) Tick(dt float64) {
|
||||
// 输入 → 玩家速度(UI与输入.md §6 动作映射)
|
||||
a.in.apply(a.sess)
|
||||
a.sess.Tick(dt)
|
||||
|
||||
// 脏区块网格重建(CPU 侧,预算内)
|
||||
start := time.Now()
|
||||
for _, c := range a.sess.World.ActiveChunks() {
|
||||
if time.Since(start) > 6*time.Millisecond {
|
||||
break // 帧预算(区块管理.md §4.3.2)
|
||||
}
|
||||
if !c.Dirty.Load() {
|
||||
continue
|
||||
}
|
||||
m := a.meshBuilder.Build(c, func(x, y, z int32) block.State {
|
||||
return a.sess.World.Block(x, y, z)
|
||||
})
|
||||
a.mu.Lock()
|
||||
a.pendingMesh = append(a.pendingMesh, &worldMeshJob{cx: c.CX, cz: c.CZ, m: m})
|
||||
a.mu.Unlock()
|
||||
c.Dirty.Store(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Render 每帧渲染(渲染线程):上传待传网格 → 视图矩阵 → 三 pass 绘制。
|
||||
func (a *clientApp) Render() {
|
||||
// 上传 tick 构建好的网格(GL 调用必须在渲染线程)
|
||||
a.mu.Lock()
|
||||
pending := a.pendingMesh
|
||||
a.pendingMesh = nil
|
||||
a.mu.Unlock()
|
||||
for _, j := range pending {
|
||||
a.rend.UploadChunk(j.cx, j.cz, j.m)
|
||||
}
|
||||
|
||||
// 视图投影
|
||||
cam := a.sess.Cam
|
||||
aspect := float64(a.cfgW()) / float64(a.cfgH())
|
||||
a.rend.SetViewProj(cam.View(), cam.Projection(aspect, 0.05, 1024))
|
||||
|
||||
// 清屏 + 绘制(视锥剔除简化为全量绘制,后置 LOD)
|
||||
a.rend.BeginFrame()
|
||||
chunks := a.sess.World.ActiveChunks()
|
||||
a.rend.DrawChunks(func(i int) (int32, int32) { return chunks[i].CX, chunks[i].CZ }, len(chunks))
|
||||
a.rend.EndFrame()
|
||||
}
|
||||
|
||||
// ShouldClose 窗口关闭判定。
|
||||
func (a *clientApp) ShouldClose() bool { return a.rend.ShouldClose() }
|
||||
|
||||
// cfgW / cfgH 窗口尺寸(渲染用)。
|
||||
func (a *clientApp) cfgW() int { return a.winW }
|
||||
func (a *clientApp) cfgH() int { return a.winH }
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
log, err := logx.New("logs/client.log", logx.LevelInfo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfg, err := config.Load("config/client.yaml", config.DefaultClient())
|
||||
if err != nil {
|
||||
log.Warnf("配置加载失败,使用默认值:%v", err)
|
||||
}
|
||||
log.Infof("%s 客户端启动:%dx%d,语言 %s,视距 %d", cfg.Client.WindowTitle, cfg.Client.WindowWidth, cfg.Client.WindowHeight, cfg.Client.Language, cfg.Client.RenderDistance)
|
||||
|
||||
// 1) 内容包与图集(设计.md §1.3:扫描 mods/ + resourcepacks/)
|
||||
am := assets.New()
|
||||
if err := am.Load("assets", "mods", "resourcepacks"); err != nil {
|
||||
log.Warnf("内容包加载失败:%v", err)
|
||||
}
|
||||
log.Infof("已加载 %d 个内容包", len(am.Packs()))
|
||||
|
||||
keys, err := am.ExpandAtlas("blocks")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
atlasImg, uvRects, err := atlas.Build(am.ResolveTexture, keys, 16)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Infof("blocks 图集:%d 纹理,%dx%d", len(uvRects), atlasImg.Bounds().Dx(), atlasImg.Bounds().Dy())
|
||||
|
||||
// UV 函数(渲染.md §4:inset 防渗色)
|
||||
uvFn := func(texKey string) (u0, v0, u1, v1 float32, ok bool) {
|
||||
rect, found := uvRects[texKey]
|
||||
if !found {
|
||||
return 0, 0, 1, 1, false
|
||||
}
|
||||
W, H := float32(atlasImg.Bounds().Dx()), float32(atlasImg.Bounds().Dy())
|
||||
const inset = 0.5
|
||||
u0 = (float32(rect.Min.X) + inset) / W
|
||||
v0 = (float32(rect.Min.Y) + inset) / H
|
||||
u1 = (float32(rect.Max.X) - inset) / W
|
||||
v1 = (float32(rect.Max.Y) - inset) / H
|
||||
return u0, v0, u1, v1, true
|
||||
}
|
||||
|
||||
// 2) 注册表(设计.md §6.1/§6.2 数据驱动)
|
||||
reg, err := block.Load("assets/config/blocks.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
items, err := item.Load("assets/config/items.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
recipes, err := recipe.Load("assets/config/recipes.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 3) 世界与生成器(区块管理.md §4 异步流水线)
|
||||
gen, err := worldgen.New(reg, 123456789, cfg.Client.RenderDistance/2000.0+0.004, 0.01, 0.1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
w, err := world.New(reg, gen, cfg.Client.RenderDistance)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
// 4) 游戏会话(玩家先高空下落,区块加载后落地)
|
||||
p := physics.NewPlayer(8.5, 100, 8.5)
|
||||
cam := camera.New(p.Eye(), 0, 0, cfg.Client.FOV)
|
||||
inv := inventory.New(items)
|
||||
sess := game.NewSession(w, reg, items, recipes, p, inv, cam)
|
||||
|
||||
// 5) 渲染器(GL 线程亲和在其内部锁定)
|
||||
rend, err := render.New(cfg.Client.WindowTitle, cfg.Client.WindowWidth, cfg.Client.WindowHeight)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rend.Shutdown()
|
||||
rend.SetAtlas(atlasImg)
|
||||
rend.SetCursorMode(true)
|
||||
|
||||
// 6) 输入(鼠标捕获 + 键鼠回调)
|
||||
in := newInputState(rend.Window())
|
||||
|
||||
app := &clientApp{
|
||||
sess: sess,
|
||||
rend: rend,
|
||||
reg: reg,
|
||||
in: in,
|
||||
meshBuilder: mesh.NewBuilder(reg, uvFn),
|
||||
winW: cfg.Client.WindowWidth,
|
||||
winH: cfg.Client.WindowHeight,
|
||||
lastTime: time.Now(),
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer stop()
|
||||
|
||||
log.Infof("进入游戏主循环")
|
||||
engine.Run(ctx, app, app)
|
||||
log.Infof("客户端已退出")
|
||||
fmt.Println("年糕历险记 客户端:本构建未启用 GL 渲染。")
|
||||
fmt.Println("请先准备 C 工具链(tools/zig 或 mingw-w64),然后用以下方式构建:")
|
||||
fmt.Println(" go build -tags \"gl dev\" ./cmd/client")
|
||||
fmt.Println("或直接运行 scripts/build.ps1。")
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ func TestBuild(t *testing.T) {
|
||||
t.Fatalf("字形 %q 宽度期望 8,实际 %d", ch, g.W)
|
||||
}
|
||||
}
|
||||
// 主位图应为 128×128(16×16 网格)
|
||||
// 主位图:宽 128(16 列 × 8px),高为 8 的整数倍(原版 ascii.png 为 128×536)
|
||||
if at.Image == nil {
|
||||
t.Fatal("主位图为空")
|
||||
}
|
||||
if b := at.Image.Bounds(); b.Dx() != 128 || b.Dy() != 128 {
|
||||
t.Fatalf("ascii 位图期望 128×128,实际 %dx%d", b.Dx(), b.Dy())
|
||||
if b := at.Image.Bounds(); b.Dx() != 128 || b.Dy()%8 != 0 || b.Dy() < 128 {
|
||||
t.Fatalf("ascii 位图尺寸异常: %dx%d", b.Dx(), b.Dy())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build gl
|
||||
|
||||
package render
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build gl
|
||||
|
||||
// Package render 渲染层:GLFW 窗口 + OpenGL 3.3 core 上下文 + 默认光影着色器(场景/渲染.md)。
|
||||
//
|
||||
// 线程铁律(渲染.md §2):
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
# 年糕历险记 构建脚本(Windows)
|
||||
# 用法:pwsh -File scripts/build.ps1 [-Release]
|
||||
# 自动准备便携 MinGW-w64(GLFW 需要 CGO),然后构建 client/server。
|
||||
# 自动定位 C 工具链(tools/ 下的 zig 或 mingw64/w64devkit),构建 GL 客户端与服务器。
|
||||
param([switch]$Release)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path $PSScriptRoot -Parent
|
||||
$tools = Join-Path $root "tools"
|
||||
$mingw = Join-Path $tools "mingw64"
|
||||
$gcc = Join-Path $mingw "bin\gcc.exe"
|
||||
|
||||
if (-not (Test-Path $gcc)) {
|
||||
Write-Host "== 未找到 C 编译器(GLFW 需要),下载便携 MinGW-w64 =="
|
||||
New-Item -ItemType Directory -Force -Path $tools | Out-Null
|
||||
$url = "https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-ucrt-r1/winlibs-x86_64-posix-seh-gcc-14.2.0-mingw-w64ucrt-12.0.0-r1.zip"
|
||||
$zip = Join-Path $env:TEMP "winlibs-mingw.zip"
|
||||
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
|
||||
Expand-Archive -Path $zip -DestinationPath $tools -Force
|
||||
Remove-Item $zip -Force
|
||||
if (-not (Test-Path $gcc)) {
|
||||
throw "MinGW 准备失败:请手动安装 mingw-w64 并重试,或检查网络。"
|
||||
# 定位 C 编译器(GLFW 需要 CGO):优先 zig(zig cc),其次 mingw64/w64devkit 的 gcc
|
||||
$cc = $null
|
||||
$zig = Get-ChildItem $tools -Recurse -Filter zig.exe -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($zig) {
|
||||
$cc = "$($zig.FullName) cc"
|
||||
Write-Host "== 使用 zig 作为 C 编译器: $($zig.FullName) =="
|
||||
} else {
|
||||
$gcc = Get-ChildItem $tools -Recurse -Filter gcc.exe -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($gcc) {
|
||||
$cc = $gcc.FullName
|
||||
$env:Path = "$(Split-Path $gcc.FullName);$env:Path"
|
||||
Write-Host "== 使用 gcc: $($gcc.FullName) =="
|
||||
} else {
|
||||
Write-Host "== 未找到 C 工具链:请把 zig(或 MinGW)放到 tools/ 后重试。"
|
||||
Write-Host " 下载:https://ziglang.org/download/(zig-x86_64-windows-*.zip)"
|
||||
Write-Host " 或: https://github.com/brechtsanders/winlibs_mingw/releases(winlibs zip)"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
$env:CC = $cc
|
||||
$env:CGO_ENABLED = "1"
|
||||
|
||||
$env:CC = $gcc
|
||||
$env:Path = "$(Join-Path $mingw 'bin');$env:Path"
|
||||
|
||||
$tags = ""
|
||||
$ldflags = "-s -w"
|
||||
if (-not $Release) { $tags = "-tags dev" }
|
||||
$tags = "gl"
|
||||
if (-not $Release) { $tags = "$tags dev" }
|
||||
|
||||
Push-Location $root
|
||||
try {
|
||||
New-Item -ItemType Directory -Force -Path bin | Out-Null
|
||||
go build $tags -trimpath -ldflags $ldflags -o bin/mc-client.exe ./cmd/client
|
||||
go build $tags -trimpath -ldflags $ldflags -o bin/mc-server.exe ./cmd/server
|
||||
go build -tags $tags -trimpath -ldflags "-s -w" -o bin/mc-client.exe ./cmd/client
|
||||
go build -trimpath -ldflags "-s -w" -o bin/mc-server.exe ./cmd/server
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
BIN
tools/zig.zip
BIN
tools/zig.zip
Binary file not shown.
Reference in New Issue
Block a user