chichilku3 14.0.3 → 15.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/bin/chichilku3 +1 -0
  3. data/bin/chichilku3-server +1 -0
  4. data/lib/client/chichilku3.rb +3 -1
  5. data/lib/client/client.rb +112 -59
  6. data/lib/client/client_cfg.rb +2 -1
  7. data/lib/client/gui.rb +219 -197
  8. data/lib/client/img/stick128/arm64/arm0.png +0 -0
  9. data/lib/client/img/stick128/arm64/arm1.png +0 -0
  10. data/lib/client/img/stick128/arm64/arm2.png +0 -0
  11. data/lib/client/img/stick128/arm64/arm3.png +0 -0
  12. data/lib/client/img/stick128/noarms/stick0.png +0 -0
  13. data/lib/client/img/stick128/noarms/stick1.png +0 -0
  14. data/lib/client/img/stick128/noarms/stick2.png +0 -0
  15. data/lib/client/img/stick128/noarms/stick3.png +0 -0
  16. data/lib/client/img/stick128/noarms/stick4.png +0 -0
  17. data/lib/client/img/stick128/noarms/stick5.png +0 -0
  18. data/lib/client/keys.rb +29 -0
  19. data/lib/client/particles.rb +54 -0
  20. data/lib/client/scoreboard.rb +29 -27
  21. data/lib/{client → external/gosu}/text.rb +29 -38
  22. data/lib/external/rubyzip/recursive.rb +58 -0
  23. data/lib/server/chichilku3_server.rb +169 -64
  24. data/lib/server/gamelogic.rb +107 -51
  25. data/lib/server/server_cfg.rb +2 -0
  26. data/lib/share/config.rb +21 -7
  27. data/lib/share/console.rb +22 -5
  28. data/lib/share/game_map.rb +279 -0
  29. data/lib/share/math.rb +14 -0
  30. data/lib/share/network.rb +47 -42
  31. data/lib/share/player.rb +168 -105
  32. data/lib/share/projectile.rb +97 -98
  33. data/lib/share/string.rb +24 -0
  34. data/server.json +3 -2
  35. metadata +49 -23
  36. data/lib/client/img/battle1024x576.png +0 -0
  37. data/lib/client/img/grass1024x512.png +0 -0
  38. data/lib/client/img/stick128/stick_noarms.png +0 -0
  39. data/lib/client/test.rb +0 -39
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
2
4
 
3
5
  require_relative '../share/console'
4
6
  require_relative '../share/network'
5
7
  require_relative '../share/player'
8
+ require_relative '../share/game_map'
6
9
 
7
10
  require_relative 'gamelogic'
8
11
  require_relative 'server_cfg'
@@ -23,12 +26,16 @@ class ServerCore
23
26
  # 1 - player id
24
27
  @clients = []
25
28
  @current_id = 0
29
+ @bans = {}
26
30
  @tick = 0
27
31
  @last_alive_pck_by_client = Time.now
28
32
  @console = Console.new
29
- @cfg = ServerCfg.new(@console, "server.json")
33
+ @cfg = ServerCfg.new(@console, 'server.json')
30
34
  @gamelogic = GameLogic.new(@console)
31
- @global_pack = nil
35
+
36
+ @cfg.data['map'] = 'battle' if @cfg.data['map'] == ''
37
+ @game_map = GameMap.new(@console, @cfg, @cfg.data['map'])
38
+ @game_map.prepare_upload
32
39
  end
33
40
 
34
41
  def parse_client_version(data)
@@ -38,10 +45,10 @@ class ServerCore
38
45
  version = data[1..4]
39
46
  player = Player.get_player_by_id(@players, id)
40
47
  if player
41
- @console.log "name req id='#{id}' vrs='#{version}' name='#{player.name}'"
48
+ @console.dbg "[NAME-REQUEST] ID='#{id}' version='#{version}' name='#{player.name}'"
42
49
  player.set_version(version)
43
50
  else
44
- @console.log "error parsing version data=#{data}"
51
+ @console.err "failed to parse version data=#{data}"
45
52
  end
46
53
  player
47
54
  end
@@ -49,7 +56,13 @@ class ServerCore
49
56
  def create_name_package(data, client)
50
57
  if !client.nil? && !data.nil?
51
58
  player = Player.get_player_by_id(@players, client[PLAYER_ID])
52
- player.set_name(data)
59
+ if player.nil?
60
+ port, ip = Socket.unpack_sockaddr_in(client[NET_CLIENT].getpeername)
61
+ @console.wrn "IP=#{ip}:#{port} tried to get a name pack (without player)"
62
+ return
63
+ end
64
+ player.name = data.strip.gsub(/[^a-zA-Z0-9_]/, '_')
65
+ @gamelogic.on_player_connect(client, @players)
53
66
  end
54
67
 
55
68
  # protocol 3 name prot
@@ -58,16 +71,15 @@ class ServerCore
58
71
  pck = "3l#{net_pack_int(@players.count)}g"
59
72
  @players.each do |p|
60
73
  pck += p.to_n_pck
61
- @console.dbg "pname='#{p.name}'"
62
74
  end
63
75
  pck.ljust(SERVER_PACKAGE_LEN, '0')
64
76
  end
65
77
 
66
- def get_free_id
78
+ def next_free_id
67
79
  # TODO: do this smarter
68
- used_ids = @clients.map{ |c| c[1] }
80
+ used_ids = @clients.map { |c| c[1] }
69
81
  id = 0
70
- while id < MAX_CLIENTS do
82
+ while id < MAX_CLIENTS
71
83
  id += 1
72
84
  return id unless used_ids.include? id
73
85
  end
@@ -75,10 +87,10 @@ class ServerCore
75
87
  end
76
88
 
77
89
  def add_player(name, version, client, ip)
78
- @current_id = get_free_id()
90
+ @current_id = next_free_id
79
91
  return -1 if @current_id > MAX_CLIENTS || @current_id < 1
80
92
 
81
- @console.log "Added player id='#{@current_id}' version='#{version}' ip='#{ip}'"
93
+ @console.dbg "[NEW PLAYER] IP='#{ip}' ID='#{@current_id}' version='#{version}'"
82
94
  @players << Player.new(@current_id, 0, nil, nil, name, version, ip)
83
95
  client[PLAYER_ID] = @current_id
84
96
  @current_id # implicit return
@@ -100,16 +112,37 @@ class ServerCore
100
112
 
101
113
  def update_pck(data, dt)
102
114
  id = data[0].to_i(16)
103
- @console.dbg "got player with id: #{id}"
104
- @players = @gamelogic.handle_client_requests(data[1..-1], id, @players, dt)
115
+ @console.dbg "[UPDATE] got player with id: #{id}"
116
+ @players = @gamelogic.handle_client_requests(@game_map, data[1..-1], id, @players, dt)
105
117
  nil # defaults to normal update pck
106
118
  end
107
119
 
120
+ def map_info_pck
121
+ "5l#{@game_map.checksum}".ljust(SERVER_PACKAGE_LEN, ' ')
122
+ end
123
+
124
+ def map_dl_init_pck
125
+ size = net_pack_bigint(@game_map.size, 6)
126
+ name = @game_map.name
127
+ "6l#{size}#{name}".ljust(SERVER_PACKAGE_LEN, ' ')
128
+ end
129
+
130
+ def map_dl_chunk_pck(player)
131
+ size = SERVER_PACKAGE_LEN - 2
132
+ map_chunk = @game_map.get_data(player.map_download, size)
133
+ player.map_download += size
134
+ "7l#{map_chunk}"
135
+ end
136
+
108
137
  def id_pck(data, client, ip)
138
+ if num_ip_connected(ip) > @cfg.data['max_clients_per_ip']
139
+ disconnect_client(client, "0l#{NET_ERR_DISCONNECT}too many clients per ip ")
140
+ return
141
+ end
109
142
  player_version = data[0..3]
110
- id = add_player("(connecting)", player_version, client, ip)
143
+ id = add_player('(connecting)', player_version, client, ip)
111
144
  if id == -1
112
- @console.log "'#{name}' failed to connect (server full)"
145
+ @console.log "IP='#{ip}' failed to connect (server full)"
113
146
  # protocol 0 (error) code=404 slot not found
114
147
  return "0l#{NET_ERR_FULL} "
115
148
  end
@@ -120,22 +153,27 @@ class ServerCore
120
153
  @console.log "IP='#{ip}' failed to connect (client too new '#{player_version}' < '#{GAME_VERSION}')"
121
154
  return "0l#{NET_ERR_SERVER_OUTDATED}#{GAME_VERSION}".ljust(SERVER_PACKAGE_LEN, ' ')
122
155
  end
123
- @console.log "id='#{id}' ip='#{ip}' joined the game"
124
- @global_pack = "true"
156
+ @console.dbg "[ID-PACKAGE] ID='#{id}' IP='#{ip}'"
125
157
  # protocol 2 (id)
126
- "2l#{net_pack_int(@players.count)}#{net_pack_int(MAX_CLIENTS)}#{id.to_s(16)}X#{GAME_VERSION}".ljust(SERVER_PACKAGE_LEN, '0')
158
+ "2l#{net_pack_int(@players.count)}#{net_pack_int(MAX_CLIENTS)}#{id.to_s(16)}X#{GAME_VERSION}".ljust(
159
+ SERVER_PACKAGE_LEN, '0'
160
+ )
127
161
  end
128
162
 
129
163
  def command_package(data, client)
130
164
  id = data[0..1].to_i(16)
131
- cmd = data[1..-1]
165
+ cmd = data[1..-1].strip
132
166
  @console.log "[chat] ID=#{id} command='#{cmd}'"
133
167
  msg = "server_recived_cmd: #{cmd}"
134
168
  msg = msg.ljust(SERVER_PACKAGE_LEN - 2, '0')
135
169
  msg = msg[0..SERVER_PACKAGE_LEN - CMD_LEN]
136
- if cmd == "test"
170
+ case cmd
171
+ when 'test'
137
172
  # return "0l#{NET_ERR_DISCONNECT} SAMPLE MESSAGE "
138
173
  msg = "id=#{client[PLAYER_ID]}"
174
+ when 'exit'
175
+ msg = 'ok'
176
+ shutdown
139
177
  end
140
178
  msg = msg.ljust(SERVER_PACKAGE_LEN - 2, ' ')
141
179
  msg = msg[0..SERVER_PACKAGE_LEN - 2]
@@ -144,27 +182,62 @@ class ServerCore
144
182
  end
145
183
 
146
184
  def handle_protocol(client, protocol, p_status, data, ip, dt)
147
- @console.dbg "HANDLE PROTOCOL=#{protocol} status=#{p_status}"
185
+ @console.dbg "[PROTOCOL] protocol=#{protocol} status=#{p_status}"
148
186
  if protocol.zero? # error pck
149
- @console.log "Error pck=#{data}"
187
+ @console.err "Error pck=#{data}"
150
188
  elsif protocol == 1 # id pck
151
- return id_pck(data, client, ip)
189
+ id_pck(data, client, ip)
152
190
  elsif protocol == 3 # initial request names
153
- return create_name_package(data, client)
191
+ create_name_package(data, client)
154
192
  else
193
+ if data.nil?
194
+ @console.err "IP=#{ip} invalid data"
195
+ return
196
+ end
155
197
  # all other types require id
156
198
  id = data[0].to_i(16)
157
199
  if id != client[PLAYER_ID]
158
- @console.log("id=#{client[PLAYER_ID]} tried to spoof id=#{id} ip=#{ip}")
200
+ @console.wrn "id=#{client[PLAYER_ID]} tried to spoof id=#{id} ip=#{ip}"
201
+ @console.wrn data
159
202
  disconnect_client(client, "0l#{NET_ERR_DISCONNECT}invalid player id ")
160
203
  return nil
161
204
  end
162
- if protocol == 2 # update pck
163
- return update_pck(data, dt)
164
- elsif protocol == 4 # command
165
- return command_package(data, client)
205
+ case protocol
206
+ when 2 # update pck
207
+ return update_pck(data, dt) if @game_map.nil?
208
+
209
+ player = Player.get_player_by_id(@players, id)
210
+ if player.map_download == -2
211
+ player.map_download = -1
212
+ map_info_pck
213
+ elsif player.map_download == -1
214
+ # set state to -3 which stops sending
215
+ # any further information
216
+ # wait for the client to respond
217
+ player.map_download = -3
218
+ map_dl_init_pck
219
+ elsif player.map_download < @game_map.size && player.map_download >= 0
220
+ map_dl_chunk_pck(player)
221
+ else
222
+ update_pck(data, dt)
223
+ end
224
+ when 4 # command
225
+ command_package(data, client)
226
+ when 5 # map info response
227
+ return update_pck(data, dt) if @game_map.nil?
228
+
229
+ player = Player.get_player_by_id(@players, id)
230
+ if data[1] == '1'
231
+ player.map_download = 0
232
+ @console.log 'player started map download'
233
+ map_dl_chunk_pck(player)
234
+ else
235
+ @console.log 'player rejected map download'
236
+ player.map_download = @game_map.size
237
+ update_pck(data, dt)
238
+ end
166
239
  else
167
- @console.log "ERROR unkown protocol=#{protocol} data=#{data}"
240
+ @console.err "IP=#{ip} unkown protocol=#{protocol} data=#{data}"
168
241
  end
169
242
  end
170
243
  end
@@ -174,32 +247,17 @@ class ServerCore
174
247
  # the response is a direct respond to an protocol
175
248
  # everything above this could override important responds
176
249
  # like id assignment
177
- # every think that is after this guad case just overrides update pcks
250
+ # everything that is after this guard case just overrides update pcks
178
251
  return response unless response.nil?
179
252
 
180
- if (@tick % 100).zero?
181
- return create_name_package(nil, nil)
182
- end
183
-
184
- # some debug suff for class vars
185
- # if (@tick % 50).zero?
186
- # puts ""
187
- # @console.log "id=#{data[0].to_i} currentid=#{@current_id}"
188
- # end
189
-
190
- # if @global_pack.nil?
191
- # @global_pack = nil
192
- # @console.log "sending an global pck"
193
- # return "5l#{players_to_packet}"
194
- # end
253
+ return create_name_package(nil, nil) if (@tick % 100).zero?
195
254
 
196
255
  # if error occurs or something unexpected
197
256
  # just send regular update pck
198
257
  # protocol 1 (update)
199
- "1l#{players_to_packet}" # implicit return
258
+ "1l#{players_to_packet}"
200
259
  end
201
260
 
202
- # TODO: this func and it dependencies should be new file
203
261
  # Handles each client
204
262
  def client_tick(cli, dt)
205
263
  client_data = save_read(cli[NET_CLIENT], CLIENT_PACKAGE_LEN)
@@ -211,9 +269,8 @@ class ServerCore
211
269
  return
212
270
  end
213
271
 
214
- @console.dbg "tick recv: '#{client_data}'"
215
272
  @last_alive_pck_by_client = Time.now
216
- port, ip = Socket.unpack_sockaddr_in(cli[NET_CLIENT].getpeername)
273
+ _port, ip = Socket.unpack_sockaddr_in(cli[NET_CLIENT].getpeername)
217
274
  server_response = handle_client_data(cli, client_data, ip, dt)
218
275
  pck_type = server_response[0]
219
276
  if pck_type == SERVER_PCK_TYPE[:error]
@@ -225,8 +282,8 @@ class ServerCore
225
282
 
226
283
  def disconnect_client(client, server_response = nil)
227
284
  player_id = client[PLAYER_ID]
228
- @console.log "player id=#{player_id} left the game." if player_id != -1
229
- @console.dbg "client disconnected.#{" (" + server_response + ")" unless server_response.nil?}"
285
+ @gamelogic.on_player_disconnect(client, @players)
286
+ @console.dbg "client disconnected.#{" (#{server_response})" unless server_response.nil?}"
230
287
  net_write(server_response, client[NET_CLIENT]) unless server_response.nil?
231
288
  client[NET_CLIENT].close
232
289
  delete_player(player_id)
@@ -240,6 +297,14 @@ class ServerCore
240
297
  end
241
298
  end
242
299
 
300
+ def shutdown
301
+ @clients.each do |client|
302
+ disconnect_client(client)
303
+ end
304
+ @console.log 'server shutdown.'
305
+ exit
306
+ end
307
+
243
308
  def run
244
309
  server = TCPServer.open(@cfg.data['port'])
245
310
  server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) # nagle's algorithm
@@ -252,39 +317,79 @@ class ServerCore
252
317
  if $next_tick > t
253
318
  sleep $next_tick - t
254
319
  else
255
- unless @tick.zero?
256
- @console.log "[WARNING] tick took #{t - $next_tick} too long"
257
- end
320
+ @console.wrn "tick took #{t - $next_tick} too long" unless @tick.zero?
258
321
  end
259
322
  $next_tick = Time.now + MAX_TICK_SPEED
260
323
  @tick += 1
261
- @players = @gamelogic.tick(@players, diff)
324
+ @players = @gamelogic.tick(@game_map, @players, diff, @tick)
262
325
  # there is no gurantee the client will tick here
263
326
  # there might be 2 gamelogic ticks and posticks
264
327
  # before the server recieves client data
265
328
  # since it is a nonblocking read and server/client are not in perfect sync
266
329
  @clients.each do |client|
267
- begin
268
- client_tick(client, diff)
269
- rescue Errno::ECONNRESET, Errno::ENOTCONN, EOFError, IOError
270
- disconnect_client(client)
271
- end
330
+ client_tick(client, diff)
331
+ rescue Errno::ECONNRESET, Errno::ENOTCONN, IOError
332
+ disconnect_client(client)
272
333
  end
273
- @players = @gamelogic.posttick(@players, diff)
334
+ @players = @gamelogic.posttick(@game_map, @players, diff)
274
335
  end
275
336
  end
276
337
 
338
+ def ban_client(client, seconds, message = 'banned')
339
+ _port, ip = Socket.unpack_sockaddr_in(client.getpeername)
340
+ ban_ip(ip, seconds, message)
341
+ net_write("0l#{NET_ERR_BAN}#{message}"[0..SERVER_PACKAGE_LEN].ljust(SERVER_PACKAGE_LEN, ' '), client)
342
+ client.close
343
+ end
344
+
345
+ def ban_ip(ip, seconds, message)
346
+ @bans[ip] = Time.now + seconds
347
+ @console.log "IP=#{ip} banned for #{seconds} seconds (#{message})"
348
+ end
349
+
350
+ def ip_banned?(ip)
351
+ return false if @bans[ip].nil?
352
+
353
+ (@bans[ip] - Time.now).positive?
354
+ end
355
+
277
356
  private
278
357
 
358
+ def num_ip_connected(ip)
359
+ connected = 0
360
+ @clients.each do |client|
361
+ _port, conencted_ip = Socket.unpack_sockaddr_in(client[NET_CLIENT].getpeername)
362
+ connected += 1 if conencted_ip == ip
363
+ end
364
+ connected
365
+ end
366
+
279
367
  def accept(server)
368
+ last_connect = Hash.new([Time.now, 0])
280
369
  Socket.accept_loop(server) do |client|
370
+ _port, ip = Socket.unpack_sockaddr_in(client.getpeername)
371
+ if ip_banned?(ip)
372
+ net_write("0l#{NET_ERR_BAN}banned".ljust(SERVER_PACKAGE_LEN, ' '), client)
373
+ client.close
374
+ next
375
+ end
376
+ diff = Time.now - last_connect[ip][0]
377
+ if diff < 3
378
+ last_connect[ip][1] += 1
379
+ if last_connect[ip][1] > 2
380
+ last_connect[ip][1] = 0
381
+ ban_client(client, 10, 'banned for 10 seconds (too many reconnects)')
382
+ next
383
+ end
384
+ end
281
385
  @clients << [client, -1]
282
- @console.log "client connected. clients connected: #{@clients.count}"
386
+ @console.log "client connected IP=#{ip} (total #{@clients.count})"
387
+ last_connect[ip][0] = Time.now
283
388
  end
284
389
  end
285
390
 
286
391
  def net_write(data, cli)
287
- @console.dbg("sending: #{data}")
392
+ # @console.dbg("sending: #{data}")
288
393
  cli.write(data)
289
394
  end
290
395
  end
@@ -1,26 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../share/math'
4
+
5
+ # high level game logic
1
6
  class GameLogic
2
7
  def initialize(console)
3
8
  @console = console
4
9
  @alive_players = 0
5
10
  end
6
11
 
12
+ def on_player_connect(client, players)
13
+ player = Player.get_player_by_id(players, client[PLAYER_ID])
14
+ return if player.nil?
15
+
16
+ port, ip = Socket.unpack_sockaddr_in(client[NET_CLIENT].getpeername)
17
+ @console.log "player joined ID=#{player.id} IP=#{ip}:#{port} name='#{player.name}'"
18
+ end
19
+
20
+ def on_player_disconnect(client, players)
21
+ player = Player.get_player_by_id(players, client[PLAYER_ID])
22
+ return if player.nil?
23
+
24
+ @console.log "player left ID=#{player.id} name='#{player.name}'"
25
+ end
26
+
7
27
  def check_collide(players, player)
8
28
  players.each do |other|
9
29
  next if other == player
10
- next if !player.collide[:down]
30
+ next unless player.collide[:down]
11
31
 
12
32
  x_force = player.check_player_collide(other)
13
- player.apply_force(x_force, -8) if !x_force.zero?
33
+ player.apply_force(x_force, -8) unless x_force.zero?
14
34
  end
15
35
  end
16
36
 
17
- def tick(players, dt)
37
+ def tick(game_map, players, dt, tick)
18
38
  players.each do |player|
19
39
  # reset values (should stay first)
20
40
  player.reset_collide
21
41
 
22
- gravity(player, dt)
42
+ gravity(game_map, player, dt, tick)
23
43
  player.tick
44
+ game_map_collision_vertical(game_map, player)
45
+ if player.dx.positive?
46
+ player.check_move_right(game_map)
47
+ elsif player.dx.negative?
48
+ player.check_move_left(game_map)
49
+ end
24
50
  player.projectile.tick(players)
25
51
  # player collsions works
26
52
  # but it eats performance and delays jumping
@@ -28,14 +54,51 @@ class GameLogic
28
54
  end
29
55
  end
30
56
 
31
- def handle_client_requests(data, id, players, dt)
57
+ def game_map_collision_vertical(game_map, player)
58
+ if player.dy.positive?
59
+ # left bottom
60
+ col = game_map.collision?(player.x / TILE_SIZE, (player.y + player.h) / TILE_SIZE)
61
+ if col
62
+ player.y = col[:y] * TILE_SIZE
63
+ player.y -= player.crouching? ? PLAYER_SIZE / 2 : PLAYER_SIZE
64
+ player.do_collide(:down, true)
65
+ end
66
+ # right bottom
67
+ col = game_map.collision?((player.x + player.w) / TILE_SIZE, (player.y + player.h) / TILE_SIZE)
68
+ if col
69
+ player.y = col[:y] * TILE_SIZE
70
+ player.y -= player.crouching? ? PLAYER_SIZE / 2 : PLAYER_SIZE
71
+ player.do_collide(:down, true)
72
+ end
73
+ elsif player.dy.negative?
74
+ # left top
75
+ col = game_map.collision?(player.x / TILE_SIZE, player.y / TILE_SIZE)
76
+ if col
77
+ player.y = (col[:y] * TILE_SIZE) + player.h
78
+ player.y += 1
79
+ player.y += PLAYER_SIZE / 2 if player.crouching?
80
+ player.do_collide(:up, true)
81
+ end
82
+ # right top
83
+ col = game_map.collision?((player.x + player.w) / TILE_SIZE, player.y / TILE_SIZE)
84
+ if col
85
+ player.y = (col[:y] * TILE_SIZE) + player.h
86
+ player.y += 1
87
+ player.y += PLAYER_SIZE / 2 if player.crouching?
88
+ player.do_collide(:up, true)
89
+ end
90
+ end
91
+ nil
92
+ end
93
+
94
+ def handle_client_requests(game_map, data, id, players, _dt)
32
95
  player = Player.get_player_by_id(players, id)
33
96
  if player.nil?
34
97
  @console.log "WARNING failed to update nil player with id=#{id}"
35
- if players.count > 0
36
- @console.log "connected players:"
98
+ if players.count.positive?
99
+ @console.log 'connected players:'
37
100
  else
38
- @console.log "no players currently connected!"
101
+ @console.log 'no players currently connected!'
39
102
  end
40
103
  players.each do |p|
41
104
  @console.log "id=#{p.id} name='#{p.name}'"
@@ -44,28 +107,37 @@ class GameLogic
44
107
  end
45
108
 
46
109
  # reset values (should stay first)
47
- player.state[:crouching] = false
110
+ player.wants_crouch = false
48
111
 
49
112
  # move request
50
113
  if data[0] == '1'
51
114
  @console.dbg "player=#{id} wants to crouch"
52
- player.state[:crouching] = true
53
- player.x -= TILE_SIZE / 4 unless player.was_crouching
115
+ player.crouch!
116
+ player.wants_crouch = true
117
+ player.x -= PLAYER_SIZE / 4 unless player.was_crouching
118
+ # TODO: why is it checking right when on left side!?
119
+ if closest_interval_side(TILE_SIZE, player.x) == SIDE_LEFT
120
+ player.check_move_right(game_map)
121
+ else
122
+ player.check_move_left(game_map)
123
+ end
54
124
  player.was_crouching = true
55
125
  end
56
126
  if data[1] == 'l'
127
+ game_map_collision_vertical(game_map, player)
57
128
  @console.dbg "player=#{id} wants to walk left"
58
- player.move_left
129
+ player.move_left(game_map)
59
130
  end
60
131
  if data[1] == 'r'
61
132
  @console.dbg "player=#{id} wants to walk right"
62
- player.move_right
133
+ game_map_collision_vertical(game_map, player)
134
+ player.move_right(game_map)
63
135
  end
64
136
  if data[2] == '1'
65
137
  @console.dbg "player=#{id} wants to jump"
66
138
  player.do_jump
67
139
  end
68
- if data[3] == '1' && player.state[:crouching] == false
140
+ if data[3] == '1' && player.crouching? == false
69
141
  @console.dbg "player=#{id} wants to fire"
70
142
  player.fire_ticks += 1
71
143
  if player.fire_ticks > 29
@@ -76,39 +148,40 @@ class GameLogic
76
148
  player.state[:fire] = 1
77
149
  end
78
150
  else
79
- if player.fire_ticks > 0
80
- dx = (player.aimX - player.x).clamp(-200, 200) / 20
81
- dy = (player.aimY - player.y).clamp(-200, 200) / 20
151
+ if player.fire_ticks.positive?
152
+ dx = (player.aim_x - player.x).clamp(-200, 200) / 20
153
+ dy = (player.aim_y - player.y).clamp(-200, 200) / 20
82
154
  dx *= (player.fire_ticks / 10).clamp(1, 3)
83
155
  dy *= (player.fire_ticks / 10).clamp(1, 3)
84
- player.projectile.fire(player.x + TILE_SIZE/4, player.y + TILE_SIZE/2, dx, dy, player)
156
+ player.projectile.fire(player.x + TILE_SIZE / 4, player.y + TILE_SIZE / 2, dx, dy, player)
85
157
  end
86
158
  player.fire_ticks = 0
87
159
  player.state[:fire] = 0
88
160
  end
89
- player.aimX = net_unpack_bigint(data[4..5])
90
- player.aimY = net_unpack_bigint(data[6..7])
91
- # player.projectile.x = player.aimX + 20
92
- # player.projectile.y = player.aimY + 20
161
+ player.aim_x = net_unpack_bigint(data[4..5])
162
+ player.aim_y = net_unpack_bigint(data[6..7])
163
+ # player.projectile.x = player.aim_x + 20
164
+ # player.projectile.y = player.aim_y + 20
93
165
 
94
- player.check_out_of_world
166
+ player.check_out_of_game_map
95
167
 
96
168
  # return updated players
97
169
  players
98
170
  end
99
171
 
100
- def posttick(players, dt)
172
+ def posttick(game_map, players, _dt)
101
173
  players.each do |player|
102
174
  # stopped crouching -> stand up
103
- if player.was_crouching && player.state[:crouching] == false
104
- player.y -= TILE_SIZE
105
- player.x += TILE_SIZE / 4
106
- player.was_crouching = false
107
- end
175
+ next unless player.was_crouching && player.wants_crouch == false
176
+
177
+ player.x += PLAYER_SIZE / 4
178
+ player.was_crouching = false
179
+ player.stop_crouch!
180
+ game_map_collision_vertical(game_map, player)
108
181
  end
109
182
  end
110
183
 
111
- def gravity(player, dt)
184
+ def gravity(game_map, player, _dt, _tick)
112
185
  if player.dead
113
186
  player.dead_ticks += 1
114
187
  player.state[:bleeding] = true
@@ -117,31 +190,14 @@ class GameLogic
117
190
  player.state[:bleeding] = false
118
191
  player.die
119
192
  end
120
- else
121
- if player.y + player.h > 384 # too far down --> die
122
- player.dead = true
123
- player.dead_ticks = 0
124
- end
125
- end
126
-
127
- # outside of the save zone
128
- if player.x < 214 || player.x > 800 || player.dead
129
- if player.y + player.h > 484
130
- # player.collide[:down] = true
131
- player.do_collide(:down, true)
132
- return
133
- end
134
- else # on the save zone
135
- if player.y + player.h > 324
136
- # player.collide[:down] = true
137
- player.do_collide(:down, true)
138
- return
139
- end
193
+ elsif game_map.death?(player.x / TILE_SIZE, (player.y + player.h) / TILE_SIZE)
194
+ player.dead = true
195
+ player.dead_ticks = 0
140
196
  end
141
197
 
142
198
  # grav = 100000 * dt
143
199
  # @console.log "grav: #{grav}"
144
200
  # player.y += grav
145
- player.dy += 2 if player.dy < 16
201
+ player.dy += 1 if player.dy < 16
146
202
  end
147
203
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require_relative '../share/config'
3
5