chichilku3 5.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.
- checksums.yaml +7 -0
- data/bin/chichilku3 +3 -0
- data/bin/chichilku3-server +3 -0
- data/client.json +6 -0
- data/lib/client/chichilku3.rb +18 -0
- data/lib/client/client.rb +289 -0
- data/lib/client/client_cfg.rb +15 -0
- data/lib/client/gui.rb +492 -0
- data/lib/client/img/background1024x512.png +0 -0
- data/lib/client/img/battle1024x576.png +0 -0
- data/lib/client/img/connecting1024x512.png +0 -0
- data/lib/client/img/grass1024x512.png +0 -0
- data/lib/client/img/grey128.png +0 -0
- data/lib/client/img/menu1920x1080.png +0 -0
- data/lib/client/img/stick128/stick0.png +0 -0
- data/lib/client/img/stick128/stick1.png +0 -0
- data/lib/client/img/stick128/stick2.png +0 -0
- data/lib/client/img/stick128/stick3.png +0 -0
- data/lib/client/img/stick128/stick4.png +0 -0
- data/lib/client/img/stick128/stick5.png +0 -0
- data/lib/client/img/stick128/stick_crouching0.png +0 -0
- data/lib/client/img/stick128/stick_crouching1.png +0 -0
- data/lib/client/img/stick128/stick_crouching2.png +0 -0
- data/lib/client/img/stick128/stick_crouching3.png +0 -0
- data/lib/client/img/stick128/stick_crouching4.png +0 -0
- data/lib/client/img/stick128/stick_crouching5.png +0 -0
- data/lib/client/scoreboard.rb +25 -0
- data/lib/client/test.rb +39 -0
- data/lib/client/text.rb +86 -0
- data/lib/server/chichilku3_server.rb +293 -0
- data/lib/server/gamelogic.rb +121 -0
- data/lib/server/server_cfg.rb +6 -0
- data/lib/share/config.rb +53 -0
- data/lib/share/console.rb +18 -0
- data/lib/share/network.rb +145 -0
- data/lib/share/player.rb +286 -0
- data/server.json +4 -0
- metadata +81 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
require_relative '../share/console'
|
4
|
+
require_relative '../share/network'
|
5
|
+
require_relative '../share/player'
|
6
|
+
|
7
|
+
require_relative 'gamelogic'
|
8
|
+
require_relative 'server_cfg'
|
9
|
+
|
10
|
+
$next_tick = Time.now
|
11
|
+
|
12
|
+
NET_CLIENT = 0
|
13
|
+
PLAYER_ID = 1
|
14
|
+
|
15
|
+
# ServerCore: should only contain the networking
|
16
|
+
# and no gamelogic
|
17
|
+
class ServerCore
|
18
|
+
def initialize
|
19
|
+
# single dimension array holding player objects
|
20
|
+
@players = []
|
21
|
+
# multi dimensional array
|
22
|
+
# 0 - client network socket
|
23
|
+
# 1 - player id
|
24
|
+
@clients = []
|
25
|
+
@current_id = 0
|
26
|
+
@tick = 0
|
27
|
+
@last_alive_pck_by_client = Time.now
|
28
|
+
@console = Console.new
|
29
|
+
@cfg = ServerCfg.new(@console, "server.json")
|
30
|
+
@gamelogic = GameLogic.new(@console)
|
31
|
+
@global_pack = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_client_version(data)
|
35
|
+
return if data.nil?
|
36
|
+
|
37
|
+
id = data[0].to_i
|
38
|
+
version = data[1..-1]
|
39
|
+
player = Player.get_player_by_id(@players, id)
|
40
|
+
if player
|
41
|
+
@console.log "name req id='#{id}' vrs='#{version}' name='#{player.name}'"
|
42
|
+
player.set_version(version)
|
43
|
+
else
|
44
|
+
@console.log "error parsing version data=#{data}"
|
45
|
+
end
|
46
|
+
player
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_name_package(data)
|
50
|
+
# protocol 3 name prot
|
51
|
+
# also includes client version
|
52
|
+
player = parse_client_version(data)
|
53
|
+
# gamestate
|
54
|
+
# |
|
55
|
+
pck = "3l#{@players.count}g"
|
56
|
+
# pck = format('3l%02d', @players.count) # old 2 digit player count
|
57
|
+
@players.each do |p|
|
58
|
+
pck += p.to_n_pck
|
59
|
+
@console.dbg "pname='#{p.name}'"
|
60
|
+
end
|
61
|
+
unless player.nil?
|
62
|
+
if player.version.to_i < GAME_VERSION.to_i
|
63
|
+
return "0l#{NET_ERR_CLIENT_OUTDATED}#{GAME_VERSION} "
|
64
|
+
elsif player.version.to_i > GAME_VERSION.to_i
|
65
|
+
return "0l#{NET_ERR_SERVER_OUTDATED}#{GAME_VERSION} "
|
66
|
+
end
|
67
|
+
end
|
68
|
+
pck.ljust(SERVER_PACKAGE_LEN, '0')
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_free_id
|
72
|
+
# TODO: do this smarter
|
73
|
+
used_ids = @clients.map{ |c| c[1] }
|
74
|
+
id = 0
|
75
|
+
while id < MAX_CLIENTS do
|
76
|
+
id += 1
|
77
|
+
return id unless used_ids.include? id
|
78
|
+
end
|
79
|
+
-1
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_player(name, client, ip)
|
83
|
+
@current_id = get_free_id()
|
84
|
+
return -1 if @current_id > MAX_CLIENTS || @current_id < 1
|
85
|
+
|
86
|
+
@console.log "Added player id='#{@current_id}' ip='#{ip}'"
|
87
|
+
@players << Player.new(@current_id, 0, nil, nil, name, ip)
|
88
|
+
client[PLAYER_ID] = @current_id
|
89
|
+
@current_id # implicit return
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete_player(id)
|
93
|
+
@console.log "Deleted player id='#{id}'"
|
94
|
+
@players.delete(Player.get_player_by_id(@players, id))
|
95
|
+
end
|
96
|
+
|
97
|
+
def players_to_packet
|
98
|
+
# old 2 digit player count
|
99
|
+
# packet = @players.empty? ? '00' : format('%02d', @players.count)
|
100
|
+
packet = @players.empty? ? '0' : @players.count.to_s
|
101
|
+
packet += 'g' # gamestate
|
102
|
+
@players.each do |player|
|
103
|
+
packet += player.to_s
|
104
|
+
end
|
105
|
+
# fill with zeros if less than 3 players online
|
106
|
+
packet.ljust(SERVER_PACKAGE_LEN - 2, '0') # implicit return
|
107
|
+
end
|
108
|
+
|
109
|
+
def update_pck(data, dt)
|
110
|
+
id = data[0].to_i
|
111
|
+
@console.dbg "got player with id: #{id}"
|
112
|
+
@players = @gamelogic.handle_client_requests(data[1..-1], id, @players, dt)
|
113
|
+
nil # defaults to normal update pck
|
114
|
+
end
|
115
|
+
|
116
|
+
def id_pck(data, client, ip)
|
117
|
+
name = data[0..5]
|
118
|
+
id = add_player(name, client, ip)
|
119
|
+
if id == -1
|
120
|
+
@console.log "'#{name}' failed to connect (server full)"
|
121
|
+
# protocol 0 (error) code=404 slot not found
|
122
|
+
return "0l#{NET_ERR_FULL} "
|
123
|
+
end
|
124
|
+
@console.log "id='#{id}' name='#{name}' joined the game"
|
125
|
+
@global_pack = "true"
|
126
|
+
# protocol 2 (id)
|
127
|
+
format('2l00%02d0000000000000000000000', id).to_s
|
128
|
+
end
|
129
|
+
|
130
|
+
def command_package(data, client)
|
131
|
+
id = data[0..1].to_i
|
132
|
+
cmd = data[1..-1]
|
133
|
+
@console.log "[chat] ID=#{id} command='#{cmd}'"
|
134
|
+
msg = "server_recived_cmd: #{cmd}"
|
135
|
+
msg = msg.ljust(SERVER_PACKAGE_LEN - 2, '0')
|
136
|
+
msg = msg[0..SERVER_PACKAGE_LEN - 3]
|
137
|
+
if cmd == "test"
|
138
|
+
# return "0l#{NET_ERR_DISCONNECT} SAMPLE MESSAGE "
|
139
|
+
msg = "id=#{client[PLAYER_ID]}"
|
140
|
+
end
|
141
|
+
msg = msg.ljust(SERVER_PACKAGE_LEN - 2, ' ')
|
142
|
+
msg = msg[0..SERVER_PACKAGE_LEN - 3]
|
143
|
+
"4l#{msg}"
|
144
|
+
end
|
145
|
+
|
146
|
+
def handle_protocol(client, protocol, p_status, data, ip, dt)
|
147
|
+
@console.dbg "HANDLE PROTOCOL=#{protocol} status=#{p_status}"
|
148
|
+
if protocol.zero? # error pck
|
149
|
+
@console.log "Error pck=#{data}"
|
150
|
+
elsif protocol == 1 # id pck
|
151
|
+
return id_pck(data, client, ip)
|
152
|
+
else
|
153
|
+
# all other types require id
|
154
|
+
id = data[0].to_i
|
155
|
+
if id != client[PLAYER_ID]
|
156
|
+
@console.log("id=#{client[PLAYER_ID]} tried to spoof id=#{id} ip=#{ip}")
|
157
|
+
disconnect_client(client, "0l#{NET_ERR_DISCONNECT}invalid player id ")
|
158
|
+
return nil
|
159
|
+
end
|
160
|
+
if protocol == 2 # update pck
|
161
|
+
return update_pck(data, dt)
|
162
|
+
elsif protocol == 3 # initial request names
|
163
|
+
return create_name_package(data)
|
164
|
+
elsif protocol == 4 # command
|
165
|
+
return command_package(data, client)
|
166
|
+
else
|
167
|
+
@console.log "ERROR unkown protocol=#{protocol} data=#{data}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def handle_client_data(client, data, ip, dt)
|
173
|
+
response = handle_protocol(client, data[0].to_i, data[1], data[2..-1], ip, dt)
|
174
|
+
# the response is a direct respond to an protocol
|
175
|
+
# everything above this could override important responds
|
176
|
+
# like id assignment
|
177
|
+
# every think that is after this guad case just overrides update pcks
|
178
|
+
return response unless response.nil?
|
179
|
+
|
180
|
+
if (@tick % 100).zero?
|
181
|
+
# return '3l0301hello02x0x0x03hax0r000'
|
182
|
+
return create_name_package(nil)
|
183
|
+
end
|
184
|
+
|
185
|
+
# some debug suff for class vars
|
186
|
+
# if (@tick % 50).zero?
|
187
|
+
# puts ""
|
188
|
+
# @console.log "id=#{data[0].to_i} currentid=#{@current_id}"
|
189
|
+
# end
|
190
|
+
|
191
|
+
# if @global_pack.nil?
|
192
|
+
# @global_pack = nil
|
193
|
+
# @console.log "sending an global pck"
|
194
|
+
# return "5l#{players_to_packet}"
|
195
|
+
# end
|
196
|
+
|
197
|
+
# if error occurs or something unexpected
|
198
|
+
# just send regular update pck
|
199
|
+
# protocol 1 (update)
|
200
|
+
"1l#{players_to_packet}" # implicit return
|
201
|
+
end
|
202
|
+
|
203
|
+
# TODO: this func and it dependencies should be new file
|
204
|
+
# Handles each client
|
205
|
+
def client_tick(cli, dt)
|
206
|
+
client_data = save_read(cli[NET_CLIENT], CLIENT_PACKAGE_LEN)
|
207
|
+
if client_data == ''
|
208
|
+
# diff = Time.now - @last_alive_pck_by_client
|
209
|
+
# if (diff > MAX_TIMEOUT)
|
210
|
+
# @console.log "sombody timed out"
|
211
|
+
# end
|
212
|
+
return
|
213
|
+
end
|
214
|
+
|
215
|
+
@console.dbg "recv: #{client_data}"
|
216
|
+
@last_alive_pck_by_client = Time.now
|
217
|
+
port, ip = Socket.unpack_sockaddr_in(cli[NET_CLIENT].getpeername)
|
218
|
+
server_response = handle_client_data(cli, client_data, ip, dt)
|
219
|
+
# server_response = '1l03011001010220020203300303'
|
220
|
+
pck_type = server_response[0]
|
221
|
+
if pck_type == SERVER_PCK_TYPE[:error]
|
222
|
+
disconnect_client(cli, server_response)
|
223
|
+
else
|
224
|
+
net_write(server_response, cli[NET_CLIENT])
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def disconnect_client(client, server_response = nil)
|
229
|
+
player_id = client[PLAYER_ID]
|
230
|
+
@console.log "player id=#{player_id} left the game." if player_id != -1
|
231
|
+
@console.dbg "client disconnected.#{" (" + server_response + ")" unless server_response.nil?}"
|
232
|
+
net_write(server_response, client[NET_CLIENT]) unless server_response.nil?
|
233
|
+
client[NET_CLIENT].close
|
234
|
+
delete_player(player_id)
|
235
|
+
@clients.delete(client)
|
236
|
+
@current_id -= 1
|
237
|
+
end
|
238
|
+
|
239
|
+
def client_by_playerid(player_id)
|
240
|
+
@clients.find do |client|
|
241
|
+
client[PLAYER_ID] == player_id
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def run
|
246
|
+
server = TCPServer.open(@cfg.data['port'])
|
247
|
+
server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) # nagle's algorithm
|
248
|
+
Thread.new do
|
249
|
+
accept(server)
|
250
|
+
end
|
251
|
+
loop do
|
252
|
+
diff = 0 # TODO: unused lmao traced it through the half source
|
253
|
+
t = Time.now
|
254
|
+
sleep $next_tick - t if $next_tick > t
|
255
|
+
@tick += 1
|
256
|
+
$next_tick = Time.now + MAX_TICK_SPEED
|
257
|
+
@players = @gamelogic.tick(@players, diff)
|
258
|
+
# there is no gurantee the client will tick here
|
259
|
+
# there might be 2 gamelogic ticks and posticks
|
260
|
+
# before the server recieves client data
|
261
|
+
# since it is a nonblocking read and server/client are not in perfect sync
|
262
|
+
@clients.each do |client|
|
263
|
+
begin
|
264
|
+
client_tick(client, diff)
|
265
|
+
rescue Errno::ECONNRESET, EOFError, IOError
|
266
|
+
disconnect_client(client)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
@players = @gamelogic.posttick(@players, diff)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
|
275
|
+
def accept(server)
|
276
|
+
Socket.accept_loop(server) do |client|
|
277
|
+
@clients << [client, -1]
|
278
|
+
@console.log "client connected. clients connected: #{@clients.count}"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def net_write(data, cli)
|
283
|
+
if data.length != SERVER_PACKAGE_LEN
|
284
|
+
@console.log "ERROR pack len: #{data.length}/#{SERVER_PACKAGE_LEN} pck: #{data}"
|
285
|
+
exit
|
286
|
+
end
|
287
|
+
@console.dbg("sending: #{data}")
|
288
|
+
cli.write(data)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
srv = ServerCore.new
|
293
|
+
srv.run
|
@@ -0,0 +1,121 @@
|
|
1
|
+
class GameLogic
|
2
|
+
def initialize(console)
|
3
|
+
@console = console
|
4
|
+
@alive_players = 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def check_collide(players, player)
|
8
|
+
players.each do |other|
|
9
|
+
next if other == player
|
10
|
+
next if !player.collide[:down]
|
11
|
+
|
12
|
+
x_force = player.check_player_collide(other)
|
13
|
+
player.apply_force(x_force, -8) if !x_force.zero?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def tick(players, dt)
|
18
|
+
players.each do |player|
|
19
|
+
# reset values (should stay first)
|
20
|
+
player.reset_collide
|
21
|
+
|
22
|
+
gravity(player, dt)
|
23
|
+
player.tick
|
24
|
+
# player collsions works
|
25
|
+
# but it eats performance and delays jumping
|
26
|
+
check_collide(players, player)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def handle_client_requests(data, id, players, dt)
|
31
|
+
player = Player.get_player_by_id(players, id)
|
32
|
+
if player.nil?
|
33
|
+
@console.log "WARNING failed to update nil player with id=#{id}"
|
34
|
+
if players.count > 0
|
35
|
+
@console.log "connected players:"
|
36
|
+
else
|
37
|
+
@console.log "no players currently connected!"
|
38
|
+
end
|
39
|
+
players.each do |p|
|
40
|
+
@console.log "id=#{p.id} name='#{p.name}'"
|
41
|
+
end
|
42
|
+
return players
|
43
|
+
end
|
44
|
+
|
45
|
+
# reset values (should stay first)
|
46
|
+
player.state[:crouching] = false
|
47
|
+
|
48
|
+
# move request
|
49
|
+
if data[0] == '1'
|
50
|
+
@console.dbg "player=#{id} wants to crouch"
|
51
|
+
player.state[:crouching] = true
|
52
|
+
player.x -= TILE_SIZE / 4 unless player.was_crouching
|
53
|
+
player.was_crouching = true
|
54
|
+
end
|
55
|
+
if data[1] == '1'
|
56
|
+
@console.dbg "player=#{id} wants to walk left"
|
57
|
+
player.move_left
|
58
|
+
end
|
59
|
+
if data[2] == '1'
|
60
|
+
@console.dbg "player=#{id} wants to walk right"
|
61
|
+
player.move_right
|
62
|
+
end
|
63
|
+
if data[3] == '1'
|
64
|
+
@console.dbg "player=#{id} wants to jump"
|
65
|
+
player.do_jump
|
66
|
+
end
|
67
|
+
|
68
|
+
player.check_out_of_world
|
69
|
+
|
70
|
+
# return updated players
|
71
|
+
players
|
72
|
+
end
|
73
|
+
|
74
|
+
def posttick(players, dt)
|
75
|
+
players.each do |player|
|
76
|
+
# stopped crouching -> stand up
|
77
|
+
if player.was_crouching && player.state[:crouching] == false
|
78
|
+
player.y -= TILE_SIZE
|
79
|
+
player.x += TILE_SIZE / 4
|
80
|
+
player.was_crouching = false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def gravity(player, dt)
|
86
|
+
if player.dead
|
87
|
+
player.dead_ticks += 1
|
88
|
+
player.state[:bleeding] = true
|
89
|
+
if player.dead_ticks > 3
|
90
|
+
player.dead = false
|
91
|
+
player.state[:bleeding] = false
|
92
|
+
player.die
|
93
|
+
end
|
94
|
+
else
|
95
|
+
if player.y > 320 # too far down --> die
|
96
|
+
player.dead = true
|
97
|
+
player.dead_ticks = 0
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# outside of the save zone
|
102
|
+
if player.x < 214 || player.x > 800 || player.dead
|
103
|
+
if player.y + player.h > 484
|
104
|
+
# player.collide[:down] = true
|
105
|
+
player.do_collide(:down, true)
|
106
|
+
return
|
107
|
+
end
|
108
|
+
else # on the save zone
|
109
|
+
if player.y + player.h > 324
|
110
|
+
# player.collide[:down] = true
|
111
|
+
player.do_collide(:down, true)
|
112
|
+
return
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# grav = 100000 * dt
|
117
|
+
# @console.log "grav: #{grav}"
|
118
|
+
# player.y += grav
|
119
|
+
player.dy += 2 if player.dy < 16
|
120
|
+
end
|
121
|
+
end
|
data/lib/share/config.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'os'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
# chichilku3 config base used by client and server
|
6
|
+
class Config
|
7
|
+
attr_reader :data
|
8
|
+
|
9
|
+
def initialize(console, file)
|
10
|
+
chichilku3_dir = ""
|
11
|
+
if OS.linux?
|
12
|
+
chichilku3_dir = "#{ENV['HOME']}/.chichilku/chichilku3/"
|
13
|
+
elsif OS.mac?
|
14
|
+
chichilku3_dir = "#{ENV['HOME']}/Library/Application Support/chichilku/chichilku3/"
|
15
|
+
# elsif OS.windows?
|
16
|
+
# chichilku3_dir = "%APPDATA%\\chichilku\\chichilku3\\"
|
17
|
+
else
|
18
|
+
puts "os not supported."
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
puts "path: " + chichilku3_dir
|
22
|
+
FileUtils.mkdir_p chichilku3_dir
|
23
|
+
create_default_cfg(file, "#{chichilku3_dir}/#{file}")
|
24
|
+
@file = chichilku3_dir + file
|
25
|
+
@console = console
|
26
|
+
@data = load
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_default_cfg(from, to)
|
30
|
+
return if File.file?(to)
|
31
|
+
|
32
|
+
tmp = JSON.parse(File.read(from))
|
33
|
+
File.open(to,"w") do |f|
|
34
|
+
f.write(tmp.to_json)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def sanitize_data(data)
|
39
|
+
data
|
40
|
+
end
|
41
|
+
|
42
|
+
def load
|
43
|
+
data = JSON.parse(File.read(@file))
|
44
|
+
data = sanitize_data(data)
|
45
|
+
data
|
46
|
+
end
|
47
|
+
|
48
|
+
def save
|
49
|
+
File.open(@file, "w") do |f|
|
50
|
+
f.write(JSON.pretty_generate(data))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
DEBUG = false
|
2
|
+
|
3
|
+
# Console used by Client and Server
|
4
|
+
class Console
|
5
|
+
def log(message)
|
6
|
+
t = Time.now
|
7
|
+
puts "[#{t.hour}:#{t.min}:#{t.sec}][log] #{message}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def dbg(message)
|
11
|
+
return unless DEBUG
|
12
|
+
|
13
|
+
t = Time.now
|
14
|
+
puts "[#{t.hour}:#{t.min}:#{t.sec}][debug] #{message}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
$console = Console.new
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'socket'
|
2
|
+
# check doc_network.rb for documentation
|
3
|
+
|
4
|
+
# update GAME_VERSION on network protocol changes
|
5
|
+
GAME_VERSION = '0005'
|
6
|
+
|
7
|
+
# game
|
8
|
+
|
9
|
+
TILE_SIZE = 64
|
10
|
+
WINDOW_SIZE_X = TILE_SIZE * 16
|
11
|
+
WINDOW_SIZE_Y = TILE_SIZE * 9
|
12
|
+
FULLHD_X = 1920
|
13
|
+
UI_SCALE = WINDOW_SIZE_X.to_f / FULLHD_X.to_f
|
14
|
+
SPEED = TILE_SIZE
|
15
|
+
|
16
|
+
# networking
|
17
|
+
|
18
|
+
NAME_LEN = 5
|
19
|
+
MAX_CLIENTS = 3
|
20
|
+
CLIENT_PACKAGE_LEN = 7 # used by server
|
21
|
+
SERVER_PACKAGE_LEN = MAX_CLIENTS * 8 + 4 # used by client
|
22
|
+
|
23
|
+
MAX_TIMEOUT = 5
|
24
|
+
MAX_TICK_SPEED = 0.01 # the lower the faster client and server tick
|
25
|
+
# MAX_TICK_SPEED = 0.005
|
26
|
+
|
27
|
+
NET_ERR_FULL = "404"
|
28
|
+
NET_ERR_DISCONNECT = "001"
|
29
|
+
NET_ERR_KICK = "002"
|
30
|
+
NET_ERR_BAN = "003"
|
31
|
+
NET_ERR_SERVER_OUTDATED = "004"
|
32
|
+
NET_ERR_CLIENT_OUTDATED = "005"
|
33
|
+
|
34
|
+
NET_ERR = {
|
35
|
+
"404" => "SERVER FULL",
|
36
|
+
"001" => "DISCONNECTED",
|
37
|
+
"002" => "KICKED",
|
38
|
+
"003" => "BANNED",
|
39
|
+
"004" => "SERVER OUTDATED",
|
40
|
+
"005" => "CLIENT OUTDATED"
|
41
|
+
}
|
42
|
+
|
43
|
+
CLIENT_PCK_TYPE = {
|
44
|
+
:error => "0",
|
45
|
+
:join => "1",
|
46
|
+
:move => "2",
|
47
|
+
:info => "3",
|
48
|
+
:cmd => "4"
|
49
|
+
}
|
50
|
+
|
51
|
+
SERVER_PCK_TYPE = {
|
52
|
+
:error => "0",
|
53
|
+
:update => "1",
|
54
|
+
# TODO: find a good name here
|
55
|
+
:info => "3",
|
56
|
+
:cmd => "4",
|
57
|
+
:event => "5"
|
58
|
+
}
|
59
|
+
|
60
|
+
NET_INT_OFFSET = 33
|
61
|
+
NET_INT_BASE = 93
|
62
|
+
NET_MAX_INT = NET_INT_BASE
|
63
|
+
NET_MIN_INT = 0
|
64
|
+
|
65
|
+
##
|
66
|
+
# Converts a integer to single character network string
|
67
|
+
#
|
68
|
+
# the base of the network is NET_INT_BASE
|
69
|
+
# so the number 93 is the last single character number represented as '~'
|
70
|
+
#
|
71
|
+
# @param [Integer, #chr] int decimal based number
|
72
|
+
# @return [String] the int converted to base NET_INT_BASE
|
73
|
+
|
74
|
+
def net_pack_int(int)
|
75
|
+
net_error "#{__method__}: '#{int}' is too low allowed range #{NET_MIN_INT}-#{NET_MAX_INT}" if int < NET_MIN_INT
|
76
|
+
net_error "#{__method__}: '#{int}' is too high allowed range #{NET_MIN_INT}-#{NET_MAX_INT}" if int > NET_MAX_INT
|
77
|
+
int = int + NET_INT_OFFSET
|
78
|
+
int.chr
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Converts a single character network string to integer
|
83
|
+
#
|
84
|
+
# the base of the network is NET_INT_BASE
|
85
|
+
# so the number 93 is the last single character number represented as '~'
|
86
|
+
#
|
87
|
+
# @param [String, #ord] net_int network packed string
|
88
|
+
# @return [Integer] the net_int converted to decimal based number
|
89
|
+
|
90
|
+
def net_unpack_int(net_int)
|
91
|
+
net_int.ord - NET_INT_OFFSET
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Converts a integer to multi character network string
|
96
|
+
#
|
97
|
+
# @param [Integer, #net_pack_int] int decimal based number
|
98
|
+
# @param [Integer] size max length of the network string
|
99
|
+
# @return [String] the int converted to base NET_INT_BASE
|
100
|
+
|
101
|
+
def net_pack_bigint(int, size)
|
102
|
+
sum = ""
|
103
|
+
div = size - 1
|
104
|
+
(size - 1).times do
|
105
|
+
buf = int / ((NET_MAX_INT+1) ** div)
|
106
|
+
sum += net_pack_int(buf)
|
107
|
+
int = int % ((NET_MAX_INT+1) ** div)
|
108
|
+
end
|
109
|
+
sum += net_pack_int(int)
|
110
|
+
# TODO: check reminder and so on
|
111
|
+
# throw and error when int is too big for size
|
112
|
+
int = int / NET_MAX_INT
|
113
|
+
sum
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Converts a multi character network string to integer
|
118
|
+
#
|
119
|
+
# @param [String, #net_unpack_int] net_int network packed int
|
120
|
+
# @return [Integer] the net_int converted to decimal based number
|
121
|
+
|
122
|
+
def net_unpack_bigint(net_int)
|
123
|
+
sum = 0
|
124
|
+
net_int.chars.reverse.each_with_index do |c, i|
|
125
|
+
if i.zero?
|
126
|
+
sum = net_unpack_int(c)
|
127
|
+
else
|
128
|
+
sum += net_unpack_int(c) * i * (NET_MAX_INT+1)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
sum
|
132
|
+
end
|
133
|
+
|
134
|
+
def save_read(socket, size)
|
135
|
+
begin
|
136
|
+
return socket.read_nonblock(size)
|
137
|
+
rescue IO::WaitReadable
|
138
|
+
return ''
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def net_error(err)
|
143
|
+
raise "NetError: #{err}"
|
144
|
+
exit 1
|
145
|
+
end
|