57 |
Current talks
58 |
59 | {currentTalks.map(({ id, title, room, start, end }) => (
60 |
61 | {title}
62 |
63 | {room} ({start.toTimeString().split(' ')[0]} - {end.toTimeString().split(' ')[0]})
64 |
65 | ))}
66 |
67 |
Next talks
68 | {nextTalks.map(({ id, title, room, start, end }) => (
69 |
70 | {title}
71 |
72 | {room} ({start.toTimeString().split(' ')[0]} - {end.toTimeString().split(' ')[0]})
73 |
74 | ))}
75 |
76 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore my test bots
2 | bots/
3 |
4 | # Ignore yarn or pnpm to keep it npm only
5 | yarn.lock
6 | pnpm-lock.yaml
7 | package-lock.json
8 |
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | lerna-debug.log*
16 | .pnpm-debug.log*
17 |
18 | # Diagnostic reports (https://nodejs.org/api/report.html)
19 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
20 |
21 | # Runtime data
22 | pids
23 | *.pid
24 | *.seed
25 | *.pid.lock
26 |
27 | # Directory for instrumented libs generated by jscoverage/JSCover
28 | lib-cov
29 |
30 | # Coverage directory used by tools like istanbul
31 | coverage
32 | *.lcov
33 |
34 | # nyc test coverage
35 | .nyc_output
36 |
37 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
38 | .grunt
39 |
40 | # Bower dependency directory (https://bower.io/)
41 | bower_components
42 |
43 | # node-waf configuration
44 | .lock-wscript
45 |
46 | # Compiled binary addons (https://nodejs.org/api/addons.html)
47 | build/Release
48 |
49 | # Dependency directories
50 | node_modules/
51 | jspm_packages/
52 |
53 | # Snowpack dependency directory (https://snowpack.dev/)
54 | web_modules/
55 |
56 | # TypeScript cache
57 | *.tsbuildinfo
58 |
59 | # Optional npm cache directory
60 | .npm
61 |
62 | # Optional eslint cache
63 | .eslintcache
64 |
65 | # Optional stylelint cache
66 | .stylelintcache
67 |
68 | # Microbundle cache
69 | .rpt2_cache/
70 | .rts2_cache_cjs/
71 | .rts2_cache_es/
72 | .rts2_cache_umd/
73 |
74 | # Optional REPL history
75 | .node_repl_history
76 |
77 | # Output of 'npm pack'
78 | *.tgz
79 |
80 | # Yarn Integrity file
81 | .yarn-integrity
82 |
83 | # dotenv environment variable files
84 | .env
85 | .env.development.local
86 | .env.test.local
87 | .env.production.local
88 | .env.local
89 |
90 | # parcel-bundler cache (https://parceljs.org/)
91 | .cache
92 | .parcel-cache
93 |
94 | # Next.js build output
95 | .next
96 | out
97 |
98 | # Nuxt.js build / generate output
99 | .nuxt
100 | dist
101 |
102 | # Gatsby files
103 | .cache/
104 | # Comment in the public line in if your project uses Gatsby and not Next.js
105 | # https://nextjs.org/blog/next-9-1#public-directory-support
106 | # public
107 |
108 | # vuepress build output
109 | .vuepress/dist
110 |
111 | # vuepress v2.x temp and cache directory
112 | .temp
113 | .cache
114 |
115 | # Docusaurus cache and generated files
116 | .docusaurus
117 |
118 | # Serverless directories
119 | .serverless/
120 |
121 | # FuseBox cache
122 | .fusebox/
123 |
124 | # DynamoDB Local files
125 | .dynamodb/
126 |
127 | # TernJS port file
128 | .tern-port
129 |
130 | # Stores VSCode versions used for testing VSCode extensions
131 | .vscode-test
132 |
133 | # yarn v2
134 | .yarn/cache
135 | .yarn/unplugged
136 | .yarn/build-state.yml
137 | .yarn/install-state.gz
138 | .pnp.*
139 |
--------------------------------------------------------------------------------
/server/ClientSocket.ts:
--------------------------------------------------------------------------------
1 | import { Socket } from "net"
2 | import { EventEmitter } from "events"
3 | //import { maxPacketsPerSecond } from "@gpn-tron/shared/constants/common"
4 |
5 | export class ClientSocket extends EventEmitter {
6 | #connected = false
7 | #ip: string
8 | #socket?: Socket
9 | #sendBuffer = ""
10 | #sendTimeout?: NodeJS.Timeout
11 |
12 | /**
13 | * Create a ClientSocket instance from a tcp socket instance
14 | * @param socket TCP Socket instance
15 | * @returns {ClientSocket | null} Returns a ClientSocket instance or null if there was an error
16 | */
17 | static fromSocket(socket: Socket) {
18 | try {
19 | return new ClientSocket(socket)
20 | } catch (error) {
21 | console.error(error)
22 | return null
23 | }
24 | }
25 |
26 | constructor(socket: Socket) {
27 | super()
28 |
29 | if (socket.destroyed) throw new Error('Socket is not connected')
30 | if (!socket.remoteAddress) throw new Error('Socket has no valid ip')
31 |
32 | this.#connected = true
33 | this.#socket = socket
34 | this.#ip = socket.remoteAddress
35 |
36 | let buffer = ''
37 |
38 | this.#socket.on('data', chunk => {
39 | // More than x packets per second can be considered as spam.
40 | // Increase packet recv counter by 1 and check if its above the max
41 | //if (this.#recvPacketCount++ > maxPacketsPerSecond) {
42 | // return this.sendError('ERROR_SPAM', true)
43 | //}
44 |
45 | // After a second reduce the packet count by one again so this packet is just counted for 1 second
46 | //setTimeout(() => {
47 | // this.#recvPacketCount--
48 | //}, 1000)
49 |
50 | buffer += chunk.toString()
51 |
52 | if (buffer.length > 1024) {
53 | this.sendError('ERROR_PACKET_OVERFLOW', true)
54 | return
55 | }
56 |
57 | while (this.#connected && buffer.includes('\n')) {
58 | const packetIndex = buffer.indexOf('\n')
59 | const packetStr = buffer.substring(0, packetIndex)
60 | buffer = buffer.substring(packetIndex + 1)
61 | this.#onPacket(packetStr)
62 | }
63 | })
64 |
65 | this.#socket.on('close', () => this.disconnect())
66 | this.#socket.on('end', () => this.disconnect())
67 | this.#socket.on('error', this.#onError.bind(this))
68 | }
69 |
70 | get connected(): boolean { return this.#connected }
71 | get ip(): string { return this.#ip }
72 |
73 | disconnect() {
74 | if (!this.#connected) return
75 |
76 | this.#connected = false
77 | this.#socket.removeAllListeners()
78 | this.#socket?.end()
79 | this.#socket?.destroy()
80 | this.#socket = undefined
81 | this.emit('disconnected')
82 | }
83 |
84 | send(type: string, ...args: any) {
85 | return this.rawSend(`${[type, ...args].join('|')}\n`)
86 | }
87 |
88 | rawSend(packet: string) {
89 | if (!this.connected || !this.#socket || this.#socket.destroyed) return
90 |
91 | this.#sendBuffer += packet
92 |
93 | clearTimeout(this.#sendTimeout)
94 | this.#sendTimeout = setTimeout(() => {
95 | try {
96 | this.#socket.write(this.#sendBuffer)
97 | this.#sendBuffer = ""
98 | }
99 | catch (error) {
100 | console.error(error)
101 | this.disconnect()
102 | }
103 | }, 1)
104 | }
105 |
106 | sendError(error: string, disconnect = false) {
107 | this.send('error', error)
108 | if (disconnect) this.disconnect()
109 | }
110 |
111 | #onPacket(packet: string) {
112 | if (!this.connected) return
113 |
114 | const args = packet.split('|').map(arg => /^\-?\d+(\.\d+)?$/.test(arg) ? Number(arg) : arg)
115 | const type = args.shift()
116 | this.emit('packet', type, ...args)
117 | }
118 |
119 | #onError(error: Error & { code: string }) {
120 | if (error?.code !== 'ECONNRESET') console.error(error)
121 | this.disconnect()
122 | }
123 | }
--------------------------------------------------------------------------------
/server/Bot.ts:
--------------------------------------------------------------------------------
1 | import { Socket } from "net"
2 |
3 | export class Bot {
4 | #socket: Socket
5 | #ip: string
6 | #port: number
7 | #connected = false
8 | #name: string
9 | #pos: Vec2 = { x: 0, y: 0 }
10 | #playerId: number
11 | #width: number
12 | #height: number
13 | #fields: Array