1
0
game/server/main.go

201 lines
5.1 KiB
Go

package main
import (
"encoding/binary"
"flag"
"fmt"
"image"
"image/color"
"image/png"
"log"
"math"
"math/rand"
"os"
"server/net"
)
const WorldSize = 100
func generateHeightmap(size int) [][]float32 {
heightmap := make([][]float32, size)
for i := range heightmap {
heightmap[i] = make([]float32, size)
}
// Simple perlin-like noise
for y := range heightmap {
for x := range heightmap[y] {
nx := float64(x) / float64(size) * 4
ny := float64(y) / float64(size) * 4
heightmap[y][x] = float32(
math.Sin(nx*2+rand.Float64())*0.5+
math.Cos(ny*3+rand.Float64())*0.3+
rand.Float64()*0.2) * 10.0
}
}
// Smooth the heightmap
for range 3 {
newHeightmap := make([][]float32, size)
for y := range newHeightmap {
newHeightmap[y] = make([]float32, size)
for x := range newHeightmap[y] {
sum := heightmap[y][x]
count := float32(1)
for dy := -1; dy <= 1; dy++ {
for dx := -1; dx <= 1; dx++ {
nx, ny := x+dx, y+dy
if nx >= 0 && nx < size && ny >= 0 && ny < size {
sum += heightmap[ny][nx]
count++
}
}
}
newHeightmap[y][x] = sum / count
}
}
heightmap = newHeightmap
}
return heightmap
}
func saveHeightmapPNG(heightmap [][]float32, filename string) error {
size := len(heightmap)
img := image.NewGray(image.Rect(0, 0, size, size))
// Find min/max for normalization
minH, maxH := heightmap[0][0], heightmap[0][0]
for y := range heightmap {
for x := range heightmap[y] {
if heightmap[y][x] < minH {
minH = heightmap[y][x]
}
if heightmap[y][x] > maxH {
maxH = heightmap[y][x]
}
}
}
// Apply normalized values to image
for y := range heightmap {
for x := range heightmap[y] {
normalized := (heightmap[y][x] - minH) / (maxH - minH)
img.SetGray(x, y, color.Gray{Y: uint8(normalized * 255)})
}
}
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("failed to create file %s: %w", filename, err)
}
defer file.Close()
if err := png.Encode(file, img); err != nil {
return fmt.Errorf("failed to encode PNG: %w", err)
}
return nil
}
func saveHeightmapBinary(heightmap [][]float32, filename string) error {
size := len(heightmap)
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("failed to create file %s: %w", filename, err)
}
defer file.Close()
if err := binary.Write(file, binary.LittleEndian, int32(size)); err != nil {
return fmt.Errorf("failed to write size: %w", err)
}
for y := range heightmap {
for x := range heightmap[y] {
if err := binary.Write(file, binary.LittleEndian, heightmap[y][x]); err != nil {
return fmt.Errorf("failed to write heightmap data: %w", err)
}
}
}
return nil
}
// loadHeightmapBinary loads a heightmap from a binary file
func loadHeightmapBinary(filename string) ([][]float32, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open file %s: %w", filename, err)
}
defer file.Close()
var size int32
if err := binary.Read(file, binary.LittleEndian, &size); err != nil {
return nil, fmt.Errorf("failed to read size: %w", err)
}
heightmap := make([][]float32, size)
for y := range heightmap {
heightmap[y] = make([]float32, size)
for x := range heightmap[y] {
if err := binary.Read(file, binary.LittleEndian, &heightmap[y][x]); err != nil {
return nil, fmt.Errorf("failed to read heightmap data: %w", err)
}
}
}
return heightmap, nil
}
func main() {
// Parse command-line flags
var (
port = flag.String("port", "9999", "UDP port to listen on")
worldSize = flag.Int("size", WorldSize, "World size for heightmap generation")
skipGen = flag.Bool("skip-gen", false, "Skip heightmap generation")
assetsPath = flag.String("assets", "../assets", "Path to assets directory")
)
flag.Parse()
// Setup logging
log.SetPrefix("[GameServer] ")
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
var heightmap [][]float32
if !*skipGen {
// Generate and save heightmap
log.Printf("Generating %dx%d heightmap...", *worldSize, *worldSize)
heightmap = generateHeightmap(*worldSize)
pngPath := fmt.Sprintf("%s/heightmap.png", *assetsPath)
if err := saveHeightmapPNG(heightmap, pngPath); err != nil {
log.Printf("Warning: Failed to save PNG heightmap: %v", err)
} else {
log.Printf("Saved heightmap PNG to %s", pngPath)
}
binPath := fmt.Sprintf("%s/heightmap.bin", *assetsPath)
if err := saveHeightmapBinary(heightmap, binPath); err != nil {
log.Fatalf("Failed to save binary heightmap: %v", err)
}
log.Printf("Saved heightmap binary to %s", binPath)
} else {
// Load existing heightmap
binPath := fmt.Sprintf("%s/heightmap.bin", *assetsPath)
log.Printf("Loading existing heightmap from %s", binPath)
var err error
heightmap, err = loadHeightmapBinary(binPath)
if err != nil {
log.Fatalf("Failed to load heightmap: %v", err)
}
}
server, err := net.NewServer(*port, heightmap)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
log.Printf("Starting game server on port %s", *port)
if err := server.Run(); err != nil {
log.Fatalf("Server failed: %v", err)
}
}