chichilku3 14.0.2 → 15.0.0

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 (38) 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/server/chichilku3_server.rb +169 -64
  22. data/lib/server/gamelogic.rb +107 -51
  23. data/lib/server/server_cfg.rb +2 -0
  24. data/lib/share/config.rb +21 -7
  25. data/lib/share/console.rb +22 -5
  26. data/lib/share/game_map.rb +279 -0
  27. data/lib/share/math.rb +14 -0
  28. data/lib/share/network.rb +47 -42
  29. data/lib/share/player.rb +168 -105
  30. data/lib/share/projectile.rb +97 -98
  31. data/lib/share/string.rb +24 -0
  32. data/server.json +3 -2
  33. metadata +44 -20
  34. data/lib/client/img/battle1024x576.png +0 -0
  35. data/lib/client/img/grass1024x512.png +0 -0
  36. data/lib/client/img/stick128/stick_noarms.png +0 -0
  37. data/lib/client/test.rb +0 -39
  38. data/lib/client/text.rb +0 -86
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ KEY_A = 4
4
+ KEY_B = 5
5
+ KEY_C = 6
6
+ KEY_D = 7
7
+ KEY_E = 8
8
+ KEY_F = 9
9
+ KEY_G = 10
10
+ KEY_H = 11
11
+ KEY_I = 12
12
+ KEY_J = 13
13
+ KEY_K = 14
14
+ KEY_L = 15
15
+ KEY_M = 16
16
+ KEY_N = 17
17
+ KEY_O = 18
18
+ KEY_P = 19
19
+ KEY_Q = 20
20
+ KEY_R = 21
21
+ KEY_S = 22
22
+ KEY_T = 23
23
+ KEY_U = 24
24
+ KEY_V = 25
25
+ KEY_W = 26
26
+ KEY_RIGHT = 79
27
+ KEY_LEFT = 80
28
+ KEY_DOWN = 81
29
+ KEY_UP = 82
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Renderer for moving textures like grass
4
+ class Particles
5
+ def initialize(console)
6
+ @console = console
7
+ @grass_img = Gosu::Image.new(img('grass/single_64.png'))
8
+ end
9
+
10
+ def draw(game_map, players)
11
+ # y = MAP_HEIGHT * TILE_SIZE - TILE_SIZE * 3
12
+ # y += TILE_SIZE / 2
13
+ # y += 2
14
+ # grass(0, TILE_SIZE * 5, y, move_x, move_y)
15
+
16
+ game_map.grass_rows.each do |grass_row|
17
+ grass(grass_row[:x1], grass_row[:x2], grass_row[:y], players)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def img(path)
24
+ File.join(File.dirname(__FILE__), '../../lib/client/img/', path)
25
+ end
26
+
27
+ def grass(x1, x2, y, players, density = 3)
28
+ # @console.log(x1.to_s + " " + x2.to_s + " " + y.to_s + " " + move_x.to_s + " " + move_y.to_s)
29
+ srand(0)
30
+ x = x1
31
+ amount = (x2 - x1) / density
32
+ amount.times do
33
+ x += density
34
+ rot = rand(-8..8)
35
+ move = false
36
+ players.each do |player|
37
+ next unless player.x > x - 10 && player.x < x + 10 && player.y > y - 128 && player.y < y + 128
38
+
39
+ move = true
40
+ break
41
+ end
42
+ if move
43
+ if rot.positive?
44
+ rot += 2
45
+ else
46
+ rot -= 2
47
+ end
48
+ @grass_img.draw_rot(x + rand(-3..3), y + rand(0..3) + 1, 0, rot)
49
+ else
50
+ @grass_img.draw_rot(x + rand(-3..3), y + rand(0..3), 0, rot)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,31 +1,33 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  def draw_scoreboard(win_size_x, win_size_y, players, font, debug)
3
- # TODO: do not compute those every frame
4
- padX = win_size_x / 3
5
- sizeX = win_size_x / 3
6
- padY = win_size_y / 6
7
- sizeY = win_size_y / 3
8
- slot_height = sizeY / MAX_CLIENTS
9
- text_scale = slot_height / 15
10
- # background
11
- draw_rect(padX, padY, sizeX, sizeY+3, 0xaa000000)
12
- # left border
13
- draw_rect(padX, padY, 3, sizeY+3, 0xaa000000)
14
- # right border
15
- draw_rect(padX + sizeX - 3, padY, 3, sizeY+3, 0xaa000000)
16
- (0..MAX_CLIENTS).each do |i|
17
- # row borders
18
- draw_rect(padX + 3, padY + (i * slot_height), sizeX - 6, 3, 0xaa000000)
19
- end
20
- players.each_with_index do | player, i |
21
- score_offset = text_scale * 10 * player.score.to_s.length
22
- dbg = 0
23
- if debug
24
- dbg += 25
25
- score_offset += 25
26
- font.draw_text(player.id, padX + 5, padY + (i * slot_height), 0, text_scale, text_scale, 0xFF00FF00)
27
- end
28
- font.draw_text(player.name, dbg + padX + 5, padY + (i * slot_height), 0, text_scale, text_scale)
29
- font.draw_text(player.score, dbg + padX + sizeX - score_offset, padY + (i * slot_height), 0, text_scale, text_scale)
4
+ # TODO: do not compute those every frame
5
+ pad_x = win_size_x / 3
6
+ size_x = win_size_x / 3
7
+ pad_y = win_size_y / 6
8
+ size_y = win_size_y / 3
9
+ slot_height = size_y / MAX_CLIENTS
10
+ text_scale = slot_height / 15
11
+ # background
12
+ draw_rect(pad_x, pad_y, size_x, size_y + 3, 0xaa000000)
13
+ # left border
14
+ draw_rect(pad_x, pad_y, 3, size_y + 3, 0xaa000000)
15
+ # right border
16
+ draw_rect(pad_x + size_x - 3, pad_y, 3, size_y + 3, 0xaa000000)
17
+ (0..MAX_CLIENTS).each do |i|
18
+ # row borders
19
+ draw_rect(pad_x + 3, pad_y + (i * slot_height), size_x - 6, 3, 0xaa000000)
20
+ end
21
+ players.each_with_index do |player, i|
22
+ score_offset = text_scale * 10 * player.score.to_s.length
23
+ dbg = 0
24
+ if debug
25
+ dbg += 25
26
+ score_offset += 25
27
+ font.draw_text(player.id, pad_x + 5, pad_y + (i * slot_height), 0, text_scale, text_scale, 0xFF00FF00)
30
28
  end
29
+ font.draw_text(player.name, dbg + pad_x + 5, pad_y + (i * slot_height), 0, text_scale, text_scale)
30
+ font.draw_text(player.score, dbg + pad_x + size_x - score_offset, pad_y + (i * slot_height), 0, text_scale,
31
+ text_scale)
32
+ end
31
33
  end
@@ -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