add files
This commit is contained in:
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module server
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require github.com/gorilla/websocket v1.5.3
|
||||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
191
main.go
Normal file
191
main.go
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PacketHandshake uint8 = 0
|
||||||
|
PacketWorld uint8 = 1
|
||||||
|
|
||||||
|
TickRate = 30
|
||||||
|
Speed = 5.0
|
||||||
|
|
||||||
|
SpawnMinX = -5.0
|
||||||
|
SpawnMaxX = 5.0
|
||||||
|
SpawnMinZ = -5.0
|
||||||
|
SpawnMaxZ = 5.0
|
||||||
|
SpawnRadius = 1.5
|
||||||
|
)
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
ID uint32
|
||||||
|
|
||||||
|
X float32
|
||||||
|
Y float32
|
||||||
|
Z float32
|
||||||
|
|
||||||
|
InputX float32
|
||||||
|
InputZ float32
|
||||||
|
|
||||||
|
Conn *websocket.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
upgrader = websocket.Upgrader{
|
||||||
|
CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
|
}
|
||||||
|
|
||||||
|
players = make(map[uint32]*Player)
|
||||||
|
playersMu sync.Mutex
|
||||||
|
nextID uint32 = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
http.HandleFunc("/ws", handleWS)
|
||||||
|
|
||||||
|
go gameLoop()
|
||||||
|
|
||||||
|
log.Println("Server running on :8080")
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleWS(w http.ResponseWriter, r *http.Request) {
|
||||||
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
playersMu.Lock()
|
||||||
|
id := nextID
|
||||||
|
nextID++
|
||||||
|
|
||||||
|
x, z := randomSpawnPosition()
|
||||||
|
|
||||||
|
player := &Player{
|
||||||
|
ID: id,
|
||||||
|
X: x,
|
||||||
|
Y: 0,
|
||||||
|
Z: z,
|
||||||
|
Conn: conn,
|
||||||
|
}
|
||||||
|
players[id] = player
|
||||||
|
playersMu.Unlock()
|
||||||
|
|
||||||
|
{
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
binary.Write(buf, binary.LittleEndian, PacketHandshake)
|
||||||
|
binary.Write(buf, binary.LittleEndian, id)
|
||||||
|
conn.WriteMessage(websocket.BinaryMessage, buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Player connected:", id, "spawn at", x, z)
|
||||||
|
|
||||||
|
go readLoop(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomSpawnPosition() (float32, float32) {
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
x := rand.Float32()*(SpawnMaxX-SpawnMinX) + SpawnMinX
|
||||||
|
z := rand.Float32()*(SpawnMaxZ-SpawnMinZ) + SpawnMinZ
|
||||||
|
|
||||||
|
if isSpawnFree(x, z) {
|
||||||
|
return x, z
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpawnFree(x, z float32) bool {
|
||||||
|
for _, p := range players {
|
||||||
|
dx := p.X - x
|
||||||
|
dz := p.Z - z
|
||||||
|
if dx*dx+dz*dz < SpawnRadius*SpawnRadius {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLoop(p *Player) {
|
||||||
|
defer func() {
|
||||||
|
playersMu.Lock()
|
||||||
|
delete(players, p.ID)
|
||||||
|
playersMu.Unlock()
|
||||||
|
|
||||||
|
p.Conn.Close()
|
||||||
|
log.Println("Player disconnected:", p.ID)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, data, err := p.Conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) < 8 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewReader(data)
|
||||||
|
binary.Read(buf, binary.LittleEndian, &p.InputX)
|
||||||
|
binary.Read(buf, binary.LittleEndian, &p.InputZ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gameLoop() {
|
||||||
|
ticker := time.NewTicker(time.Second / TickRate)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
dt := float32(1.0 / TickRate)
|
||||||
|
|
||||||
|
for range ticker.C {
|
||||||
|
update(dt)
|
||||||
|
broadcast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(dt float32) {
|
||||||
|
playersMu.Lock()
|
||||||
|
defer playersMu.Unlock()
|
||||||
|
|
||||||
|
for _, p := range players {
|
||||||
|
p.X += p.InputX * Speed * dt
|
||||||
|
p.Z += p.InputZ * Speed * dt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func broadcast() {
|
||||||
|
playersMu.Lock()
|
||||||
|
defer playersMu.Unlock()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
binary.Write(buf, binary.LittleEndian, PacketWorld)
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint16(len(players)))
|
||||||
|
|
||||||
|
for _, p := range players {
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.ID)
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.X)
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.Y)
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := buf.Bytes()
|
||||||
|
|
||||||
|
for _, p := range players {
|
||||||
|
p.Conn.WriteMessage(websocket.BinaryMessage, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user