-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.rb
126 lines (109 loc) · 3.34 KB
/
server.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
require 'sinatra'
require 'puma'
require 'json'
require 'pry'
require 'faye/websocket'
require_relative 'models/catan'
Dir.glob('./models/*.rb').each { |f| require f }
$connections_by_game = {}
class Catan
class Server < Sinatra::Base
before do
if request.env['CONTENT_TYPE'] == 'application/json'
params.merge!(JSON.parse(request.body.read))
end
end
get '/' do
@games = $connections_by_game.keys
erb :games
end
post '/games' do
new_game = Game.new(
id: SecureRandom.uuid,
side_length: new_game_param(:board_size),
n_players: new_game_param(:n_players),
max_cards: new_game_param(:max_cards),
points_to_win: new_game_param(:points_to_win)
)
$connections_by_game[new_game] = []
redirect_to new_game
end
get '/games/:id' do
redirect '/' unless current_game
redirect_to current_game unless current_player
erb :game
end
get '/games/:id/board' do
redirect '/' unless current_game
redirect_to current_game unless current_player
{ html: erb(:board), data: current_game.as_json }.to_json
end
post '/games/:id/messages' do
current_game.messages << [current_player.color, params['message']]
broadcast('message', html: erb(:messages), data: current_game.as_json)
erb :messages
end
post '/games/:id/actions' do
data = params['data']
data = JSON.parse(params['data']) if data.is_a?(String)
begin
current_game.perform_action(current_player, data['action'], data['args'])
game_json = current_game.as_json
board_html = erb(:board)
message_html = erb(:messages)
broadcast('action', html: board_html, data: game_json)
broadcast('message', html: message_html, data: game_json)
status 200
{ html: board_html, data: game_json }.to_json
rescue CatanError => e
body e.message
status 400
end
end
def redirect_to(game)
redirect "/games/#{game.id}?color=#{game.players.sample.color}"
end
def current_game
@game ||= $connections_by_game.keys.detect{|g| g.id == params['id'] }
end
def current_player
@player ||= current_game && current_game.players.detect{|p| p.color == params['color']}
end
def broadcast(event, data)
message = JSON.generate([event, data])
$connections_by_game[current_game].each do |ws|
ws.send(message) # TODO: customize message per color
end
end
def new_game_param(param)
value = params[:game][param]
return [[value.to_i, 2].max, 20].min if value && value != ''
raise
rescue
3
end
end
class SocketMiddleware
UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
def initialize(app)
@app = app
end
def call(env)
if Faye::WebSocket.websocket?(env)
ws = Faye::WebSocket.new(env)
game = $connections_by_game.keys.detect { |g| g.id == env['REQUEST_PATH'][UUID_REGEX] }
ws.on :open do |event|
$connections_by_game[game] << ws
ws.send(JSON.generate(['action', data: game.as_json]))
end
ws.on :close do |event|
$connections_by_game[game].delete(ws)
ws = nil
end
ws.rack_response
else
@app.call(env)
end
end
end
end