14. 2D game development library
C++-based, with Ruby wrapper
OS X, Windows and Linux
Works with MRI, MacRuby, Rubinius
(but not JRuby)
Sunday, 9 June, 13
21. require 'gosu'
class GameWindow < Gosu::Window
def initialize
super 640, 480, false
self.caption = "Tutorial 2"
@background_image = Gosu::Image.new(self, "bg1.jpg", true)
end
def update
end
def draw
@background_image.draw(0, 0, 0)
end
end
window = GameWindow.new
window.show
x
y
z
Sunday, 9 June, 13
24. class Player
def initialize(window)
@image = Image.new window, "plane.png", false
@x = @y = @vel_x = @vel_y = @angle = 0.0
end
def warp(x, y)
@x, @y = x, y
end
def turn_left
@angle -= 5
end
def turn_right
@angle += 5
end
def accelerate
@vel_x += offset_x(@angle, 5)
@vel_y += offset_y(@angle, 5)
end
def move
@x += @vel_x
@y += @vel_y
@x %= 640
@y %= 480
@vel_x, @vel_y = 0, 0
end
def draw
@image.draw_rot(@x, @y, 1, @angle)
end
end
@angle
5
offset_y
offset_x
Sunday, 9 June, 13
25. class GameWindow < Window
def initialize
super(640, 480, false)
self.caption = "Tutorial 3"
@background_image = Image.new(self, "bg1.jpg", true)
@player = Player.new(self)
@player.warp(320, 240)
end
def update
@player.turn_left if button_down? KbLeft
@player.turn_right if button_down? KbRight
@player.accelerate if button_down? KbUp
@player.move
end
def draw
@player.draw
@background_image.draw(0, 0, 0)
end
end
Sunday, 9 June, 13
26. class GameWindow < Window
def initialize
super(640, 480, false)
self.caption = "Tutorial 3"
@background_image = Image.new(self, "bg1.jpg", true)
@player = Player.new(self)
@player.warp(320, 240)
end
def update
@player.turn_left if button_down? KbLeft
@player.turn_right if button_down? KbRight
@player.accelerate if button_down? KbUp
@player.move
end
def draw
@player.draw
@background_image.draw(0, 0, 0)
end
end
create player
Sunday, 9 June, 13
27. class GameWindow < Window
def initialize
super(640, 480, false)
self.caption = "Tutorial 3"
@background_image = Image.new(self, "bg1.jpg", true)
@player = Player.new(self)
@player.warp(320, 240)
end
def update
@player.turn_left if button_down? KbLeft
@player.turn_right if button_down? KbRight
@player.accelerate if button_down? KbUp
@player.move
end
def draw
@player.draw
@background_image.draw(0, 0, 0)
end
end
place him in middle of screen
Sunday, 9 June, 13
28. class GameWindow < Window
def initialize
super(640, 480, false)
self.caption = "Tutorial 3"
@background_image = Image.new(self, "bg1.jpg", true)
@player = Player.new(self)
@player.warp(320, 240)
end
def update
@player.turn_left if button_down? KbLeft
@player.turn_right if button_down? KbRight
@player.accelerate if button_down? KbUp
@player.move
end
def draw
@player.draw
@background_image.draw(0, 0, 0)
end
end
} move according to
user input
Sunday, 9 June, 13
29. class GameWindow < Window
def initialize
super(640, 480, false)
self.caption = "Tutorial 3"
@background_image = Image.new(self, "bg1.jpg", true)
@player = Player.new(self)
@player.warp(320, 240)
end
def update
@player.turn_left if button_down? KbLeft
@player.turn_right if button_down? KbRight
@player.accelerate if button_down? KbUp
@player.move
end
def draw
@player.draw
@background_image.draw(0, 0, 0)
end
end
draw the player
Sunday, 9 June, 13
38. Sprites
An image or animation that’s overlaid
on the background
Use single sprites (as before) or use
sprite sheets
Sprites normally represented by a
square image
Sunday, 9 June, 13
39. Sprite sheet
A bunch of images in a single file, used
as sprites
Often placed in sequence, image can be
retrieved from knowing the location
Reduces memory usage and increase
drawing speed
Sunday, 9 June, 13
50. Design
Real-time vs turn-based (immediate
response)
Latency (speed) is critical
‘Dead’ players can still observe the game
Game spectators
Sunday, 9 June, 13
51. Design
Client-server
All artifacts are local
Only messages sent back and forth the
client-server
Minimal size messages
Messages sent from client -> server once
every frame refresh
Sunday, 9 June, 13
52. Design
Server should have minimal processing, all
game logic should be in the client
Server should only receive messages and
broadcast to all clients
Messages not compressed/encoded (takes
time at the server)
Don’t send useless messages
Sunday, 9 June, 13
55. Tank 1 starts, sends
message to server
Game
Server
Tank1
object:tank1
object:tank1
Tank 1 ignores
messages that is
about itself
Sunday, 9 June, 13
56. Server simply stores and broadcasts all
messages sent to it to reduce processing
Logic to process or ignore messages are
in the client
Sunday, 9 June, 13
60. Shot goes out of range
Game
Server
Tank1
object:tank1
object:tank1
Tank2
object:tank2
object:tank1
object:tank2
object:tank2
delete:shot1
delete:shot1
delete:shot1
When the shot goes out of
range, Tank 1 sends a delete
message to the server,
broadcasted to everyone
Sunday, 9 June, 13
61. Tank 1 shot hits Tank 2
Game
Server
Tank1
object:tank1
object:tank1
Tank2
object:tank2
object:tank1
object:tank2
object:tank2
object:shot1
object:shot1
object:shot1
If Tank 1’s shot hits Tank 2,
reduce hit points from Tank1
Sunday, 9 June, 13
64. Messages are string delimited with
vertical bar (|)
Messages are accumulated till and sent
only 1 time in a frame refresh
Messages from client -> server :
message type + sprite
Message from server -> client : sprite
only
Sunday, 9 June, 13
73. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
Sunday, 9 June, 13
74. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
store my previous coordinates
Sunday, 9 June, 13
75. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
move!
Sunday, 9 June, 13
76. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
go back to previous
coordinates if I hit the
wall, go out or hit
another tank
Sunday, 9 June, 13
77. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
add me to the list of
messages to send to
server
Sunday, 9 June, 13
78. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
check the other shots
on screen to see if it
hits me, if it does, tell
the server I was hit
Sunday, 9 June, 13
79. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
move my shots, if it
hits the wall or goes
out, remove it
Sunday, 9 June, 13
80. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
if not, tell the server
its new position
Sunday, 9 June, 13
81. def update
begin
move_tank
px, py = @me.x, @me.y
@me.move
@me.warp_to(px, py) if @me.hit_wall? or @me.outside_battlefield?
@other_tanks.each do |player, tank|
@me.warp_to(px, py) if tank.alive? and @me.collide_with?(tank, 30)
end
add_to_message_queue('obj', @me)
@other_shots.each_value do |shot|
if @me.alive? and @me.collide_with?(shot, 16)
@me.hit
add_to_message_queue('obj', @me)
end
end
@me_shots.each do |shot|
shot.move # move the bullet
if shot.hit_wall? or shot.outside_battlefield?
@me_shots.delete shot
add_to_message_queue('del', shot)
else
add_to_message_queue('obj', shot)
end
end
@client.send_message @messages.join("n")
@messages.clear
all my actions are
processed, now to
send messages to
server
Sunday, 9 June, 13
84. if msg = @client.read_message
@valid_sprites.clear
data = msg.split("n")
data.each do |row|
sprite = row.split("|")
if sprite.size == 9
player = sprite[3]
@valid_sprites << sprite[0]
case sprite[1]
when 'tank'
unless player == @player
if @other_tanks[player]
@other_tanks[player].points = sprite[7].to_i
@other_tanks[player].warp_to(sprite[4], sprite[5], sprite[6])
else
@other_tanks[player] = Tank.from_sprite(self, sprite)
end
else
@me.points = sprite[7].to_i
end
when 'shot'
unless player == @player
shot = Shot.from_sprite(self, sprite)
@other_shots[shot.uuid] = shot
shot.warp_to(sprite[4], sprite[5], sprite[6])
end
end
end
end
Sunday, 9 June, 13
85. if msg = @client.read_message
@valid_sprites.clear
data = msg.split("n")
data.each do |row|
sprite = row.split("|")
if sprite.size == 9
player = sprite[3]
@valid_sprites << sprite[0]
case sprite[1]
when 'tank'
unless player == @player
if @other_tanks[player]
@other_tanks[player].points = sprite[7].to_i
@other_tanks[player].warp_to(sprite[4], sprite[5], sprite[6])
else
@other_tanks[player] = Tank.from_sprite(self, sprite)
end
else
@me.points = sprite[7].to_i
end
when 'shot'
unless player == @player
shot = Shot.from_sprite(self, sprite)
@other_shots[shot.uuid] = shot
shot.warp_to(sprite[4], sprite[5], sprite[6])
end
end
end
end
read messages from the server
Sunday, 9 June, 13
86. if msg = @client.read_message
@valid_sprites.clear
data = msg.split("n")
data.each do |row|
sprite = row.split("|")
if sprite.size == 9
player = sprite[3]
@valid_sprites << sprite[0]
case sprite[1]
when 'tank'
unless player == @player
if @other_tanks[player]
@other_tanks[player].points = sprite[7].to_i
@other_tanks[player].warp_to(sprite[4], sprite[5], sprite[6])
else
@other_tanks[player] = Tank.from_sprite(self, sprite)
end
else
@me.points = sprite[7].to_i
end
when 'shot'
unless player == @player
shot = Shot.from_sprite(self, sprite)
@other_shots[shot.uuid] = shot
shot.warp_to(sprite[4], sprite[5], sprite[6])
end
end
end
end
parse the server messages into
sprites
Sunday, 9 June, 13
87. if msg = @client.read_message
@valid_sprites.clear
data = msg.split("n")
data.each do |row|
sprite = row.split("|")
if sprite.size == 9
player = sprite[3]
@valid_sprites << sprite[0]
case sprite[1]
when 'tank'
unless player == @player
if @other_tanks[player]
@other_tanks[player].points = sprite[7].to_i
@other_tanks[player].warp_to(sprite[4], sprite[5], sprite[6])
else
@other_tanks[player] = Tank.from_sprite(self, sprite)
end
else
@me.points = sprite[7].to_i
end
when 'shot'
unless player == @player
shot = Shot.from_sprite(self, sprite)
@other_shots[shot.uuid] = shot
shot.warp_to(sprite[4], sprite[5], sprite[6])
end
end
end
end
for tank sprites other than me,
set the properties and move it
Sunday, 9 June, 13
88. if msg = @client.read_message
@valid_sprites.clear
data = msg.split("n")
data.each do |row|
sprite = row.split("|")
if sprite.size == 9
player = sprite[3]
@valid_sprites << sprite[0]
case sprite[1]
when 'tank'
unless player == @player
if @other_tanks[player]
@other_tanks[player].points = sprite[7].to_i
@other_tanks[player].warp_to(sprite[4], sprite[5], sprite[6])
else
@other_tanks[player] = Tank.from_sprite(self, sprite)
end
else
@me.points = sprite[7].to_i
end
when 'shot'
unless player == @player
shot = Shot.from_sprite(self, sprite)
@other_shots[shot.uuid] = shot
shot.warp_to(sprite[4], sprite[5], sprite[6])
end
end
end
end
only time the server tells
me about my changes is
when I’m hit
Sunday, 9 June, 13
89. if msg = @client.read_message
@valid_sprites.clear
data = msg.split("n")
data.each do |row|
sprite = row.split("|")
if sprite.size == 9
player = sprite[3]
@valid_sprites << sprite[0]
case sprite[1]
when 'tank'
unless player == @player
if @other_tanks[player]
@other_tanks[player].points = sprite[7].to_i
@other_tanks[player].warp_to(sprite[4], sprite[5], sprite[6])
else
@other_tanks[player] = Tank.from_sprite(self, sprite)
end
else
@me.points = sprite[7].to_i
end
when 'shot'
unless player == @player
shot = Shot.from_sprite(self, sprite)
@other_shots[shot.uuid] = shot
shot.warp_to(sprite[4], sprite[5], sprite[6])
end
end
end
end
move the shot
sprites
Sunday, 9 June, 13
90. if msg = @client.read_message
@valid_sprites.clear
data = msg.split("n")
data.each do |row|
sprite = row.split("|")
if sprite.size == 9
player = sprite[3]
@valid_sprites << sprite[0]
case sprite[1]
when 'tank'
unless player == @player
if @other_tanks[player]
@other_tanks[player].points = sprite[7].to_i
@other_tanks[player].warp_to(sprite[4], sprite[5], sprite[6])
else
@other_tanks[player] = Tank.from_sprite(self, sprite)
end
else
@me.points = sprite[7].to_i
end
when 'shot'
unless player == @player
shot = Shot.from_sprite(self, sprite)
@other_shots[shot.uuid] = shot
shot.warp_to(sprite[4], sprite[5], sprite[6])
end
end
end
end
Sunday, 9 June, 13
92. @other_shots.delete_if do |uuid, shot|
!@valid_sprites.include?(uuid)
end
@other_tanks.delete_if do |user, tank|
!@valid_sprites.include?(tank.uuid)
end
end
if shots and tanks (other than myself)
weren’t broadcast from the server, this
means they’ve been removed
Sunday, 9 June, 13
97. Event-driven IO library based on
Celluloid
Duck types Ruby IO classes (TCPSocket,
TCPServer etc)
Celluloid combines OO with concurrent
programming, simplifies building
multithreaded programs
Sunday, 9 June, 13
98. require 'celluloid/io'
class Arena
include Celluloid::IO
finalizer :shutdown
def initialize(host, port)
puts "Starting Tanks Arena at #{host}:#{port}."
@server = TCPServer.new(host, port)
@sprites = Hash.new
@players = Hash.new
async.run
end
def shutdown
@server.close if @server
end
def run
loop { async.handle_connection @server.accept }
end
Sunday, 9 June, 13
99. require 'celluloid/io'
class Arena
include Celluloid::IO
finalizer :shutdown
def initialize(host, port)
puts "Starting Tanks Arena at #{host}:#{port}."
@server = TCPServer.new(host, port)
@sprites = Hash.new
@players = Hash.new
async.run
end
def shutdown
@server.close if @server
end
def run
loop { async.handle_connection @server.accept }
end
What to do when the
server terminates
Sunday, 9 June, 13
100. require 'celluloid/io'
class Arena
include Celluloid::IO
finalizer :shutdown
def initialize(host, port)
puts "Starting Tanks Arena at #{host}:#{port}."
@server = TCPServer.new(host, port)
@sprites = Hash.new
@players = Hash.new
async.run
end
def shutdown
@server.close if @server
end
def run
loop { async.handle_connection @server.accept }
end
Run the Arena object
in a new thread
Sunday, 9 June, 13
101. require 'celluloid/io'
class Arena
include Celluloid::IO
finalizer :shutdown
def initialize(host, port)
puts "Starting Tanks Arena at #{host}:#{port}."
@server = TCPServer.new(host, port)
@sprites = Hash.new
@players = Hash.new
async.run
end
def shutdown
@server.close if @server
end
def run
loop { async.handle_connection @server.accept }
end
When a client connects,
handle the connection in
a new thread
Sunday, 9 June, 13
102. def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
@players[user] = message[1..9] unless @players[user]
@sprites[message[1]] = message[1..9]
when 'del'
@sprites.delete message[1]
end
end
.
.
.
Sunday, 9 June, 13
103. def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
@players[user] = message[1..9] unless @players[user]
@sprites[message[1]] = message[1..9]
when 'del'
@sprites.delete message[1]
end
end
.
.
.
Uniquely
identifies a user
Sunday, 9 June, 13
104. def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
@players[user] = message[1..9] unless @players[user]
@sprites[message[1]] = message[1..9]
when 'del'
@sprites.delete message[1]
end
end
.
.
.
Get data from
the client
Sunday, 9 June, 13
105. def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
@players[user] = message[1..9] unless @players[user]
@sprites[message[1]] = message[1..9]
when 'del'
@sprites.delete message[1]
end
end
.
.
.
Add to list of players
if player is new
Sunday, 9 June, 13
106. def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
@players[user] = message[1..9] unless @players[user]
@sprites[message[1]] = message[1..9]
when 'del'
@sprites.delete message[1]
end
end
.
.
.
Add to list of
sprites in
this server
Sunday, 9 June, 13
107. def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined the arena."
loop do
data = socket.readpartial(4096)
data_array = data.split("n")
if data_array and !data_array.empty?
begin
data_array.each do |row|
message = row.split("|")
if message.size == 10
case message[0]
when 'obj'
@players[user] = message[1..9] unless @players[user]
@sprites[message[1]] = message[1..9]
when 'del'
@sprites.delete message[1]
end
end
.
.
.
Remove sprite
from this server
Sunday, 9 June, 13
108. .
.
.
response = String.new
@sprites.each_value do |sprite|
(response << sprite.join("|") << "n") if sprite
end
socket.write response
end
rescue Exception => exception
puts exception.backtrace
end
end # end data
end # end loop
rescue EOFError => err
player = @players[user]
puts "#{player[3]} has left arena."
@sprites.delete player[0]
@players.delete user
socket.close
end
end
Sunday, 9 June, 13
109. .
.
.
response = String.new
@sprites.each_value do |sprite|
(response << sprite.join("|") << "n") if sprite
end
socket.write response
end
rescue Exception => exception
puts exception.backtrace
end
end # end data
end # end loop
rescue EOFError => err
player = @players[user]
puts "#{player[3]} has left arena."
@sprites.delete player[0]
@players.delete user
socket.close
end
end
Send list of sprites
to the client
Sunday, 9 June, 13
110. .
.
.
response = String.new
@sprites.each_value do |sprite|
(response << sprite.join("|") << "n") if sprite
end
socket.write response
end
rescue Exception => exception
puts exception.backtrace
end
end # end data
end # end loop
rescue EOFError => err
player = @players[user]
puts "#{player[3]} has left arena."
@sprites.delete player[0]
@players.delete user
socket.close
end
end
If client disconnects,
remove the player
and sprite
Sunday, 9 June, 13
111. server, port = ARGV[0] || "0.0.0.0", ARGV[1] || 1234
supervisor = Arena.supervise(server, port.to_i)
trap("INT") do
supervisor.terminate
exit
end
sleep
Sunday, 9 June, 13
112. server, port = ARGV[0] || "0.0.0.0", ARGV[1] || 1234
supervisor = Arena.supervise(server, port.to_i)
trap("INT") do
supervisor.terminate
exit
end
sleep
Monitors and restarts
the server if it crashes
Sunday, 9 June, 13
113. server, port = ARGV[0] || "0.0.0.0", ARGV[1] || 1234
supervisor = Arena.supervise(server, port.to_i)
trap("INT") do
supervisor.terminate
exit
end
sleep
Nothing for the main
thread to do so, sleep and
let the other threads run
Sunday, 9 June, 13
117. Run more than 1 game server?
Provide custom maps and sprites for
every server?
Manage and monitor game servers (not
through a console)?
Sunday, 9 June, 13
120. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Sunday, 9 June, 13
121. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Start server at
any of these ports
Sunday, 9 June, 13
122. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Registry of all arenas
Sunday, 9 June, 13
123. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Start arena
Sunday, 9 June, 13
124. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Register arena
Sunday, 9 June, 13
125. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Terminate arena
Sunday, 9 June, 13
126. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Let client know
about the
customizations
Sunday, 9 June, 13
127. configure do
@@port_range = (10000..11000).to_a
end
get "/" do
@arenas = Celluloid::Actor.all
haml :arenas
end
post "/arena/start" do
port = @@port_range.delete @@port_range.sample
arena = Arena.new(request.host, port, request.port)
arena.map_url = params[:map_url]
arena.spritesheet_url = params[:spritesheet_url]
arena.default_hp = params[:default_hp].to_i
Celluloid::Actor[:"arena_#{port}"] = arena
redirect "/"
end
get "/arena/stop/:name" do
raise "No such arena" unless Celluloid::Actor[params[:name].to_sym]
Celluloid::Actor[params[:name].to_sym].terminate
redirect "/"
end
get "/config/:name" do
arena = Celluloid::Actor[params[:name].to_sym]
array = [arena.map_url, arena.spritesheet_url,
arena.default_hp.to_s].join("|")
end
Sunday, 9 June, 13