Skip to content

Commit 89be012

Browse files
committed
Adding Golang support
1 parent aa7e5f3 commit 89be012

25 files changed

Lines changed: 1066 additions & 379 deletions

Makefile

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,46 @@
44
# Install dependencies
55
install:
66
npm install
7+
go get
78

89
# Launch watch
910
watch:
1011
npx webpack --watch --mode=development
1112

1213
# Build lib
13-
build:
14+
build: build-server@dev
1415
npx webpack --mode=production
1516

17+
build-server@dev:
18+
go build -o ./bin/server cmd/main.go
19+
20+
build-server@staging: export GOOS=linux
21+
build-server@staging: export GOARCH=arm64
22+
build-server@staging:
23+
go build -o ./bin/server cmd/main.go
24+
25+
build-server@production: export GOOS=linux
26+
build-server@production: export GOARCH=amd64
27+
build-server@production:
28+
go build -o ./bin/server cmd/main.go
29+
1630
# Launch demo client
1731
demo-client:
1832
php -S 0.0.0.0:8000 -t .
1933

2034
# Launch demo server
2135
demo-server:
36+
2237
node ./demo-server.js 8002
38+
## Start server
39+
#demo-server-go: export GODEBUG=gctrace=1
40+
demo-server-go:
41+
go run demo-server.go
2342

2443
# Lint and code style fix
2544
lint:
2645
npx eslint src/* --ext .js,.json --fix
46+
gofmt -s -w .
2747

2848
# Test
2949
test: build

demo-client.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
window.addEventListener('load', () => {
2-
const { Client, BinaryEncoder, Codec, Int8Codec, Int16Codec, LongIntCodec, BooleanCodec, StringCodec } = netcode;
2+
const { Client, BinaryEncoder, Codec, Int8Codec, Int16Codec, Int64Codec, LongIntCodec, BooleanCodec, StringCodec, LongStringCodec } = netcode;
33

44
// Register your events
55
const encoder = new BinaryEncoder([
6-
['id', new Int16Codec()],
6+
['id', new Int8Codec()],
77
['ping', new LongIntCodec(6)],
88
['pong', new LongIntCodec(6)],
99
['inverse', new BooleanCodec()],
10-
['greeting', new StringCodec()],
10+
['greeting', new LongStringCodec()],
1111
['total', new Int8Codec()],
1212
]);
1313

@@ -17,14 +17,16 @@ window.addEventListener('load', () => {
1717

1818
// Listen for a "pong" event
1919
client.on('pong', ({ detail: pong }) => {
20-
console.info('pong: %s ms', pong - ping);
20+
console.info('pong: %s ms (%s - %s)', pong - ping, pong, ping);
2121
});
2222

2323
// Listen for an "id" event
2424
client.on('id', ({ detail: id }) => {
2525
console.log('connected with id %s', id);
2626
ping = Date.now();
2727

28+
console.log('sending ping: %s', ping);
29+
2830
// Send a "ping" event
2931
client.send('ping', ping);
3032
});
@@ -41,7 +43,8 @@ window.addEventListener('load', () => {
4143
console.log('Inverse received: %s', status);
4244

4345
// Send a "greeting" event
44-
client.send('greeting', 'Hello, I\'m client 😊!');
46+
client.send('greeting', 'Hello, I\'m client 😊! Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut imperdiet molestie libero, ut sollicitudin tortor dignissim quis. Nulla iaculis nisi turpis, a malesuada nibh faucibus a. Nunc tellus lorem, varius sit amet tellus eu, dictum consectetur nulla.');
47+
// client.send('greeting', 'Hi!');
4548
});
4649

4750
// Listen for a "greeting" event

demo-server.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"log"
6+
"math"
7+
"netcode/internal/codec"
8+
"netcode/internal/server"
9+
"time"
10+
)
11+
12+
type DemoServer struct {
13+
server *server.Server
14+
}
15+
16+
func main() {
17+
port := flag.Int("port", 8002, "Port to run on")
18+
19+
flag.Parse()
20+
21+
encoder := codec.CreateBinaryEncoder([]*codec.RegisteredCodec{
22+
{0, "id", codec.UInt8Codec{}},
23+
{1, "ping", codec.LongUIntCodec{6}},
24+
{2, "pong", codec.LongUIntCodec{6}},
25+
{3, "inverse", codec.BooleanCodec{}},
26+
{4, "greeting", codec.LongStringCodec{}},
27+
{5, "total", codec.UInt8Codec{}},
28+
}, codec.UInt8Codec{})
29+
30+
clients := server.CreateClientDirectory(uint(math.Pow(2, 8)))
31+
server := server.CreateServer(*port, encoder, clients);
32+
33+
demo := DemoServer{&server}
34+
35+
go demo.run()
36+
37+
demo.server.Start()
38+
}
39+
40+
func (demo *DemoServer) run() {
41+
log.Printf("Demo is running")
42+
for {
43+
select {
44+
case m := <-demo.server.In:
45+
switch m.Message.Name {
46+
case "ping":
47+
demo.handlePing(m.Client, m.Message)
48+
case "greeting":
49+
demo.handleGreeting(m.Client, m.Message)
50+
default:
51+
log.Printf("[client #%d] '%s': %v", m.Client.ID, m.Message.Name, m.Message.Data)
52+
}
53+
case e := <-demo.server.Out:
54+
switch e.Name {
55+
case "client:join":
56+
demo.onClientJoin(e.Data.(*server.Client))
57+
case "client:leave":
58+
demo.onClientLeave(e.Data.(*server.Client))
59+
default:
60+
log.Printf("event '%s': %v", e.Name, e.Data)
61+
}
62+
}
63+
}
64+
}
65+
66+
func (demo *DemoServer) broadcastTotal() {
67+
message := codec.Message{"total", uint8(demo.server.Clients.Count())}
68+
buf := demo.server.Encoder.Encode(message)
69+
demo.server.Clients.ForEach(func(c *server.Client) { c.Write(buf) })
70+
}
71+
72+
func (demo *DemoServer) onClientJoin(c *server.Client) {
73+
log.Printf("Client #%d joined.", c.ID)
74+
c.Send(codec.Message{"id", uint8(c.ID)})
75+
demo.broadcastTotal()
76+
}
77+
78+
func (demo *DemoServer) onClientLeave(c *server.Client) {
79+
log.Printf("Client #%d left.", c.ID)
80+
demo.broadcastTotal()
81+
}
82+
83+
func (demo *DemoServer) handlePing(c *server.Client, m codec.Message) {
84+
log.Printf("[client #%d] ping %d", c.ID, m.Data)
85+
c.Send(codec.Message{"pong", uint(time.Now().UnixMilli())})
86+
c.Send(codec.Message{"inverse", true})
87+
}
88+
89+
func (demo *DemoServer) handleGreeting(c *server.Client, m codec.Message) {
90+
log.Printf("Client #%d greets you: '%s'", c.ID, m.Data)
91+
c.Send(codec.Message{"greeting", "Hello, I'm server! 😊 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut imperdiet molestie libero, ut sollicitudin tortor dignissim quis. Nulla iaculis nisi turpis, a malesuada nibh faucibus a. Nunc tellus lorem, varius sit amet tellus eu, dictum consectetur nulla."})
92+
}

demo-server.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const encoder = new BinaryEncoder([
1111
]);
1212

1313
// Create the server
14-
const port = process.argv[2];
14+
const port = process.argv[2] || 8002;
1515
const server = new Server(port, '127.0.0.1', encoder, 3);
1616

1717
// Listen for new clients
@@ -35,9 +35,9 @@ server.on('client:join', client => {
3535

3636
// Listen for "greeting" event
3737
client.on('greeting', message => {
38-
console.log('Client %s geets you: "%s"', client.id, message);
38+
console.log('Client %s greets you: "%s"', client.id, message);
3939
// Send a "greeting" event
40-
client.send('greeting', 'Hello, I\'m server 😊!');
40+
client.send('greeting', 'Hello, I\'m server! 😊 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut imperdiet molestie libero, ut sollicitudin tortor dignissim quis. Nulla iaculis nisi turpis, a malesuada nibh faucibus a. Nunc tellus lorem, varius sit amet tellus eu, dictum consectetur nulla.');
4141
});
4242

4343
// Send event "id" to the client

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module netcode
2+
3+
go 1.22.4
4+
5+
require github.com/gorilla/websocket v1.5.3

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
2+
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
3+
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
4+
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=

internal/codec/bool.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package codec
2+
3+
import (
4+
"bytes"
5+
)
6+
7+
type BooleanCodec struct {
8+
}
9+
10+
func (c BooleanCodec) encode(buffer *bytes.Buffer, data any) {
11+
if data.(bool) {
12+
buffer.WriteByte(1)
13+
} else {
14+
buffer.WriteByte(0)
15+
}
16+
}
17+
18+
func (c BooleanCodec) decode(buffer *bytes.Buffer) any {
19+
return buffer.Next(1)[0] > 0
20+
}

internal/codec/encoder.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package codec
2+
3+
import (
4+
"bytes"
5+
)
6+
7+
type BinaryEncoder struct {
8+
idCodec Codec
9+
codecsByName map[string]*RegisteredCodec
10+
codecsById map[uint8]*RegisteredCodec
11+
}
12+
13+
type Codec interface {
14+
encode(buffer *bytes.Buffer, data any)
15+
decode(buffer *bytes.Buffer) any
16+
}
17+
18+
type RegisteredCodec struct {
19+
Id uint8
20+
Name string
21+
Handler Codec
22+
}
23+
24+
type Message struct {
25+
Name string
26+
Data any
27+
}
28+
29+
func CreateBinaryEncoder(codecs []*RegisteredCodec, idCodec Codec) *BinaryEncoder {
30+
codecsByName := make(map[string]*RegisteredCodec)
31+
codecsById := make(map[uint8]*RegisteredCodec)
32+
33+
for _, codec := range codecs {
34+
codecsById[codec.Id] = codec
35+
codecsByName[codec.Name] = codec
36+
}
37+
38+
return &BinaryEncoder{
39+
idCodec: idCodec,
40+
codecsByName: codecsByName,
41+
codecsById: codecsById,
42+
}
43+
}
44+
45+
func (e BinaryEncoder) Encode(message Message) []byte {
46+
var buffer bytes.Buffer
47+
48+
codec := e.codecsByName[message.Name]
49+
50+
e.idCodec.encode(&buffer, codec.Id)
51+
codec.Handler.encode(&buffer, message.Data)
52+
53+
return buffer.Bytes()
54+
}
55+
56+
func (e BinaryEncoder) Decode(data []byte) Message {
57+
var buffer = bytes.NewBuffer(data)
58+
id := e.idCodec.decode(buffer).(uint8)
59+
codec := e.codecsById[id]
60+
61+
return Message{
62+
Name: codec.Name,
63+
Data: codec.Handler.decode(buffer),
64+
}
65+
}

internal/codec/string.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package codec
2+
3+
import (
4+
"bytes"
5+
)
6+
7+
type StringCodec struct {
8+
}
9+
10+
func (c StringCodec) encode(buffer *bytes.Buffer, data any) {
11+
length := len(data.(string))
12+
buffer.WriteByte(uint8(length))
13+
buffer.WriteString(data.(string))
14+
}
15+
16+
func (c StringCodec) decode(buffer *bytes.Buffer) any {
17+
length := int(buffer.Next(1)[0])
18+
19+
return string(buffer.Next(length))
20+
}

internal/codec/string_long.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package codec
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
)
7+
8+
type LongStringCodec struct {
9+
}
10+
11+
func (c LongStringCodec) encode(buffer *bytes.Buffer, data any) {
12+
length := len(data.(string))
13+
14+
b := make([]byte, 2)
15+
binary.BigEndian.PutUint16(b, uint16(length))
16+
buffer.Write(b)
17+
18+
buffer.WriteString(data.(string))
19+
}
20+
21+
func (c LongStringCodec) decode(buffer *bytes.Buffer) any {
22+
length := binary.BigEndian.Uint16(buffer.Next(2))
23+
24+
return string(buffer.Next(int(length)))
25+
}

0 commit comments

Comments
 (0)