11 Commits

41 changed files with 880008 additions and 86 deletions

30
Dockerfile.build Normal file
View File

@ -0,0 +1,30 @@
FROM golang:1.23
# Install build dependencies
RUN apt-get update && apt-get install -y \
gcc-mingw-w64 \
cmake \
zip \
libasound2-dev \
mesa-common-dev \
libx11-dev \
libxrandr-dev \
libxi-dev \
xorg-dev \
libgl1-mesa-dev \
libglu1-mesa-dev \
libwayland-dev \
wayland-protocols \
libxkbcommon-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
# Copy build scripts
COPY . /build/
# Set execute permissions
RUN chmod +x /build/scripts/build.sh
# Build command
CMD ["make", "all"]

30
Makefile Normal file
View File

@ -0,0 +1,30 @@
.PHONY: all clean windows linux darwin
include scripts/platforms.mk
BINARY_NAME=goonscape
VERSION=1.0.0
BUILD_DIR=build
ASSETS_DIR=resources
all: clean $(PLATFORMS)
$(PLATFORMS):
@echo "Building for $@..."
@mkdir -p $(BUILD_DIR)/$@
@scripts/build.sh $(word 1,$(subst /, ,$@)) $(word 2,$(subst /, ,$@)) \
$(BUILD_DIR)/$@/$(BINARY_NAME)$(if $(findstring windows,$@),.exe,)
@cp -r $(ASSETS_DIR) $(BUILD_DIR)/$@/
@cd $(BUILD_DIR) && zip -r $(BINARY_NAME)-$(word 1,$(subst /, ,$@))-$(word 2,$(subst /, ,$@))-v$(VERSION).zip $@
@echo "Done building for $@"
clean:
rm -rf $(BUILD_DIR)
# Development build for current platform
dev:
go build -o $(BINARY_NAME)
# Run tests
test:
go test ./...

View File

@ -63,6 +63,26 @@ go run main.go -addr somehost:6970 # Uses somehost:6970
Note: The `-local` flag is a shorthand for `-addr localhost:6969` and cannot be used together with `-addr`. Note: The `-local` flag is a shorthand for `-addr localhost:6969` and cannot be used together with `-addr`.
## Building Release Binaries
The project uses Docker to create consistent builds across platforms. To build release binaries:
1. Build the Docker image (only needed once):
```bash
sudo docker build -t goonscape-builder -f Dockerfile.build .
```
2. Create release builds:
```bash
sudo docker run -v $(pwd):/build goonscape-builder
```
This will create zip files in the `build` directory for:
- Windows (64-bit): `goonscape-windows-amd64-v1.0.0.zip`
- Linux (64-bit): `goonscape-linux-amd64-v1.0.0.zip`
Each zip contains the binary and all required assets.
## Development ## Development
The project uses Protocol Buffers for network communication. If you modify the `.proto` files, regenerate the Go code with: The project uses Protocol Buffers for network communication. If you modify the `.proto` files, regenerate the Go code with:

Binary file not shown.

Binary file not shown.

BIN
build/linux/amd64/goonscape Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
# Blender 3.6.0 MTL File: 'None'
# www.blender.org
newmtl Material.001
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd coomer.png

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -0,0 +1,12 @@
# Blender 3.6.0 MTL File: 'None'
# www.blender.org
newmtl Material.001
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd goonion.png

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -0,0 +1,12 @@
# Blender 3.6.0 MTL File: 'None'
# www.blender.org
newmtl Material.001
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd shreke.png

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
build/windows/amd64/goonscape.exe Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
# Blender 3.6.0 MTL File: 'None'
# www.blender.org
newmtl Material.001
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd coomer.png

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -0,0 +1,12 @@
# Blender 3.6.0 MTL File: 'None'
# www.blender.org
newmtl Material.001
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd goonion.png

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -0,0 +1,12 @@
# Blender 3.6.0 MTL File: 'None'
# www.blender.org
newmtl Material.001
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd shreke.png

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -168,13 +168,12 @@ func (c *Chat) Update() (string, bool) {
c.inputBuffer = c.inputBuffer[:0] c.inputBuffer = c.inputBuffer[:0]
c.cursorPos = 0 c.cursorPos = 0
c.isTyping = false c.isTyping = false
return message, true return message, true
} }
c.isTyping = false c.isTyping = false
} }
if rl.IsKeyPressed(rl.KeyEscape) { if rl.IsKeyPressed(rl.KeyEscape) && c.isTyping {
c.inputBuffer = c.inputBuffer[:0] c.inputBuffer = c.inputBuffer[:0]
c.cursorPos = 0 c.cursorPos = 0
c.isTyping = false c.isTyping = false

View File

@ -1,6 +1,7 @@
package game package game
import ( import (
"os"
"time" "time"
"gitea.boner.be/bdnugget/goonscape/assets" "gitea.boner.be/bdnugget/goonscape/assets"
@ -16,6 +17,8 @@ type Game struct {
Models []types.ModelAsset Models []types.ModelAsset
Music rl.Music Music rl.Music
Chat *Chat Chat *Chat
MenuOpen bool
QuitChan chan struct{} // Channel to signal shutdown
} }
func New() *Game { func New() *Game {
@ -27,6 +30,7 @@ func New() *Game {
Speed: 50.0, Speed: 50.0,
TargetPath: []types.Tile{}, TargetPath: []types.Tile{},
UserData: nil, UserData: nil,
QuitDone: make(chan struct{}),
}, },
OtherPlayers: make(map[int32]*types.Player), OtherPlayers: make(map[int32]*types.Player),
Camera: rl.Camera3D{ Camera: rl.Camera3D{
@ -37,6 +41,7 @@ func New() *Game {
Projection: rl.CameraPerspective, Projection: rl.CameraPerspective,
}, },
Chat: NewChat(), Chat: NewChat(),
QuitChan: make(chan struct{}),
} }
game.Player.UserData = game game.Player.UserData = game
game.Chat.userData = game game.Chat.userData = game
@ -59,6 +64,17 @@ func (g *Game) LoadAssets() error {
} }
func (g *Game) Update(deltaTime float32) { func (g *Game) Update(deltaTime float32) {
// Handle ESC for menu
if rl.IsKeyPressed(rl.KeyEscape) {
g.MenuOpen = !g.MenuOpen
return
}
// Don't process other inputs if menu is open
if g.MenuOpen {
return
}
if message, sent := g.Chat.Update(); sent { if message, sent := g.Chat.Update(); sent {
g.Player.Lock() g.Player.Lock()
g.Player.ActionQueue = append(g.Player.ActionQueue, &pb.Action{ g.Player.ActionQueue = append(g.Player.ActionQueue, &pb.Action{
@ -125,7 +141,7 @@ func (g *Game) DrawPlayer(player *types.Player, model rl.Model) {
rl.DrawModel(model, playerPos, 16, rl.White) rl.DrawModel(model, playerPos, 16, rl.White)
if player.FloatingMessage != nil && time.Now().Before(player.FloatingMessage.ExpireTime) { if player.FloatingMessage != nil {
screenPos := rl.GetWorldToScreen(rl.Vector3{ screenPos := rl.GetWorldToScreen(rl.Vector3{
X: playerPos.X, X: playerPos.X,
Y: playerPos.Y + 24.0, Y: playerPos.Y + 24.0,
@ -133,8 +149,6 @@ func (g *Game) DrawPlayer(player *types.Player, model rl.Model) {
}, g.Camera) }, g.Camera)
player.FloatingMessage.ScreenPos = screenPos player.FloatingMessage.ScreenPos = screenPos
} else if player.FloatingMessage != nil {
player.FloatingMessage = nil
} }
if len(player.TargetPath) > 0 { if len(player.TargetPath) > 0 {
@ -168,6 +182,7 @@ func (g *Game) Render() {
} }
rl.EndMode3D() rl.EndMode3D()
// Draw floating messages
drawFloatingMessage := func(msg *types.FloatingMessage) { drawFloatingMessage := func(msg *types.FloatingMessage) {
if msg == nil || time.Now().After(msg.ExpireTime) { if msg == nil || time.Now().After(msg.ExpireTime) {
return return
@ -196,8 +211,17 @@ func (g *Game) Render() {
drawFloatingMessage(other.FloatingMessage) drawFloatingMessage(other.FloatingMessage)
} }
rl.DrawFPS(10, 10) // Draw menu if open
if g.MenuOpen {
g.DrawMenu()
}
// Only draw chat if menu is not open
if !g.MenuOpen {
g.Chat.Draw(int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight())) g.Chat.Draw(int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight()))
}
rl.DrawFPS(10, 10)
rl.EndDrawing() rl.EndDrawing()
} }
@ -223,3 +247,68 @@ func (g *Game) HandleInput() {
} }
} }
} }
func (g *Game) DrawMenu() {
screenWidth := float32(rl.GetScreenWidth())
screenHeight := float32(rl.GetScreenHeight())
// Semi-transparent background
rl.DrawRectangle(0, 0, int32(screenWidth), int32(screenHeight), rl.ColorAlpha(rl.Black, 0.7))
// Menu title
title := "Menu"
titleSize := int32(40)
titleWidth := rl.MeasureText(title, titleSize)
rl.DrawText(title, int32(screenWidth/2)-titleWidth/2, 100, titleSize, rl.White)
// Menu buttons
buttonWidth := float32(200)
buttonHeight := float32(40)
buttonY := float32(200)
buttonSpacing := float32(60)
menuItems := []string{"Resume", "Settings", "Exit Game"}
for _, item := range menuItems {
buttonRect := rl.Rectangle{
X: screenWidth/2 - buttonWidth/2,
Y: buttonY,
Width: buttonWidth,
Height: buttonHeight,
}
// Check mouse hover
mousePoint := rl.GetMousePosition()
mouseHover := rl.CheckCollisionPointRec(mousePoint, buttonRect)
// Draw button
if mouseHover {
rl.DrawRectangleRec(buttonRect, rl.ColorAlpha(rl.White, 0.3))
if rl.IsMouseButtonPressed(rl.MouseLeftButton) {
switch item {
case "Resume":
g.MenuOpen = false
case "Settings":
// TODO: Implement settings
case "Exit Game":
g.Shutdown()
}
}
}
// Draw button text
textSize := int32(20)
textWidth := rl.MeasureText(item, textSize)
textX := int32(buttonRect.X+buttonRect.Width/2) - textWidth/2
textY := int32(buttonRect.Y + buttonRect.Height/2 - float32(textSize)/2)
rl.DrawText(item, textX, textY, textSize, rl.White)
buttonY += buttonSpacing
}
}
func (g *Game) Shutdown() {
close(g.QuitChan)
<-g.Player.QuitDone
rl.CloseWindow()
os.Exit(0)
}

4
go.mod
View File

@ -5,7 +5,7 @@ go 1.23.0
require ( require (
gitea.boner.be/bdnugget/goonserver v0.0.0-20250113131525-49e23114973c gitea.boner.be/bdnugget/goonserver v0.0.0-20250113131525-49e23114973c
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b
google.golang.org/protobuf v1.36.2 google.golang.org/protobuf v1.36.3
) )
require ( require (
@ -14,4 +14,4 @@ require (
golang.org/x/sys v0.29.0 // indirect golang.org/x/sys v0.29.0 // indirect
) )
// replace gitea.boner.be/bdnugget/goonserver => ./goonserver replace gitea.boner.be/bdnugget/goonserver => ./goonserver

16
go.sum
View File

@ -1,24 +1,12 @@
gitea.boner.be/bdnugget/goonserver v0.0.0-20250113131525-49e23114973c h1:TO14y5QeQXn6sLCv6vORVdjnMn5hP/Vd+60UjqcrtFA=
gitea.boner.be/bdnugget/goonserver v0.0.0-20250113131525-49e23114973c/go.mod h1:inR1bKrr/vcTba+G1KzmmY6vssMq9oGNOk836VwPa4c=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/gen2brain/raylib-go/raylib v0.0.0-20240930075631-c66f9e2942fe h1:mInjrbJkUglTM7tBmXG+epnPCE744aj15J7vjJwM4gs=
github.com/gen2brain/raylib-go/raylib v0.0.0-20240930075631-c66f9e2942fe/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q=
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b h1:JJfspevP3YOXcSKVABizYOv++yMpTJIdPUtoDzF/RWw= github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b h1:JJfspevP3YOXcSKVABizYOv++yMpTJIdPUtoDzF/RWw=
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=

View File

@ -30,6 +30,7 @@ func main() {
} }
rl.InitWindow(1024, 768, "GoonScape") rl.InitWindow(1024, 768, "GoonScape")
rl.SetExitKey(0)
defer rl.CloseWindow() defer rl.CloseWindow()
rl.InitAudioDevice() rl.InitAudioDevice()
defer rl.CloseAudioDevice() defer rl.CloseAudioDevice()
@ -51,7 +52,7 @@ func main() {
game.Player.Model = game.Models[modelIndex].Model game.Player.Model = game.Models[modelIndex].Model
game.Player.Texture = game.Models[modelIndex].Texture game.Player.Texture = game.Models[modelIndex].Texture
go network.HandleServerCommunication(conn, playerID, game.Player, game.OtherPlayers) go network.HandleServerCommunication(conn, playerID, game.Player, game.OtherPlayers, game.QuitChan)
rl.PlayMusicStream(game.Music) rl.PlayMusicStream(game.Music)
rl.SetMusicVolume(game.Music, 0.5) rl.SetMusicVolume(game.Music, 0.5)
@ -64,4 +65,7 @@ func main() {
game.Update(deltaTime) game.Update(deltaTime)
game.Render() game.Render()
} }
// Wait for clean shutdown
<-game.QuitChan
} }

View File

@ -56,15 +56,33 @@ func ConnectToServer() (net.Conn, int32, error) {
return conn, playerID, nil return conn, playerID, nil
} }
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player) { func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player, quitChan <-chan struct{}) {
// Create a buffered reader for the connection
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
actionTicker := time.NewTicker(types.ClientTickRate) actionTicker := time.NewTicker(types.ClientTickRate)
defer actionTicker.Stop() defer actionTicker.Stop()
defer conn.Close()
defer close(player.QuitDone)
// Create a channel to signal when goroutines are done
done := make(chan struct{})
go func() { go func() {
for range actionTicker.C { for {
select {
case <-quitChan:
// Send disconnect message to server
disconnectMsg := &pb.ActionBatch{
PlayerId: playerID,
Actions: []*pb.Action{{
Type: pb.Action_DISCONNECT,
PlayerId: playerID,
}},
}
writeMessage(conn, disconnectMsg)
done <- struct{}{}
return
case <-actionTicker.C:
player.Lock() player.Lock()
if len(player.ActionQueue) > 0 { if len(player.ActionQueue) > 0 {
actions := make([]*pb.Action, len(player.ActionQueue)) actions := make([]*pb.Action, len(player.ActionQueue))
@ -87,9 +105,26 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
player.Unlock() player.Unlock()
} }
} }
}
}() }()
for { for {
select {
case <-quitChan:
done := make(chan struct{})
go func() {
<-done
close(player.QuitDone)
}()
select {
case <-done:
time.Sleep(100 * time.Millisecond)
case <-time.After(1 * time.Second):
log.Println("Shutdown timed out")
}
return
default:
// Read message length (4 bytes) // Read message length (4 bytes)
lengthBuf := make([]byte, 4) lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil { if _, err := io.ReadFull(reader, lengthBuf); err != nil {
@ -142,6 +177,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
} }
} }
} }
}
// Helper function to write length-prefixed messages // Helper function to write length-prefixed messages
func writeMessage(conn net.Conn, msg proto.Message) error { func writeMessage(conn net.Conn, msg proto.Message) error {

24
scripts/build.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
# Main build process
build() {
local os=$1
local arch=$2
local output=$3
# Set CGO flags for static linking
export CGO_ENABLED=1
export GOOS=$os
export GOARCH=$arch
# Platform specific flags
if [ "$os" = "windows" ]; then
export CC=x86_64-w64-mingw32-gcc
export CXX=x86_64-w64-mingw32-g++
fi
go build -buildvcs=false -ldflags="-s -w" -o $output
}
# Call build with provided arguments
build "$1" "$2" "$3"

1
scripts/platforms.mk Normal file
View File

@ -0,0 +1 @@
PLATFORMS=windows/amd64 linux/amd64

View File

@ -27,8 +27,9 @@ type Player struct {
CurrentTick int64 CurrentTick int64
LastUpdateTime time.Time LastUpdateTime time.Time
InterpolationProgress float32 InterpolationProgress float32
UserData interface{} // Used to store reference to game UserData interface{}
FloatingMessage *FloatingMessage FloatingMessage *FloatingMessage
QuitDone chan struct{}
} }
type ModelAsset struct { type ModelAsset struct {