chingu 0.9rc4 → 0.9rc5

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{chingu}
8
- s.version = "0.9rc4"
8
+ s.version = "0.9rc5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["ippa"]
12
- s.date = %q{2011-03-07}
12
+ s.date = %q{2011-05-28}
13
13
  s.description = %q{OpenGL accelerated 2D game framework for Ruby. Builds on Gosu (Ruby/C++) which provides all the core functionality. Chingu adds simple yet powerful game states, prettier input handling, deployment safe asset-handling, a basic re-usable game object and stackable game logic.}
14
14
  s.email = %q{ippa@rubylicio.us}
15
15
  s.extra_rdoc_files = [
@@ -16,6 +16,7 @@ class Game < Chingu::Window
16
16
  @server = Server.new
17
17
 
18
18
  @server.start("0.0.0.0", 1234)
19
+ #@client.connect("192.168.0.1", 1234)
19
20
  @client.connect("127.0.0.1", 1234)
20
21
  end
21
22
 
@@ -50,6 +51,10 @@ class Client < GameStates::NetworkClient
50
51
  puts "Client Got: #{msg.inspect}"
51
52
  end
52
53
 
54
+ def on_connection_refused
55
+ puts "connection refused"
56
+ end
57
+
53
58
  end
54
59
 
55
60
  #
@@ -38,7 +38,7 @@ require_all "#{CHINGU_ROOT}/chingu/async_tasks"
38
38
  require_all "#{CHINGU_ROOT}/chingu"
39
39
 
40
40
  module Chingu
41
- VERSION = "0.9rc4"
41
+ VERSION = "0.9rc5"
42
42
 
43
43
  DEBUG_COLOR = Gosu::Color.new(0xFFFF0000)
44
44
  DEBUG_ZORDER = 9999
@@ -118,7 +118,7 @@ module Chingu
118
118
  # Disable automatic calling of update() and update_trait() each game loop
119
119
  #
120
120
  def pause!
121
- @parent.game_objects.pause_game_object(self) if @parent && @paused == false
121
+ @parent.game_objects.pause_game_object(self) if @parent && !@paused
122
122
  @paused = true
123
123
  end
124
124
  alias :pause :pause!
@@ -127,7 +127,7 @@ module Chingu
127
127
  # Enable automatic calling of update() and update_trait() each game loop
128
128
  #
129
129
  def unpause!
130
- @parent.game_objects.unpause_game_object(self) if @parent && @paused == true
130
+ @parent.game_objects.unpause_game_object(self) if @parent && @paused
131
131
  @paused = false
132
132
  end
133
133
  alias :unpause :unpause!
@@ -28,7 +28,7 @@ module Chingu
28
28
  # Uses nonblocking polling TCP and YAML to communicate.
29
29
  # If your game state inherits from NetworkClient you'll have the following methods available:
30
30
  #
31
- # connect(ip, port) # Start a blocking connection period. only connect() uses previosly given ip:port
31
+ # connect(ip, port) # Start a nonblocking connection. only connect() uses previosly given ip:port
32
32
  # send_data(data) # Send raw data on the network, nonblocking
33
33
  # send_msg(whatever ruby data) # Will get YAML'd and sent to server
34
34
  # handle_incoming_data(max_size) # Nonblocking read of incoming server data
@@ -72,6 +72,8 @@ module Chingu
72
72
  class NetworkClient < Chingu::GameState
73
73
  attr_reader :latency, :socket, :packet_counter, :packet_buffer, :ip, :port
74
74
  alias_method :address, :ip
75
+
76
+ def connected?; @connected; end
75
77
 
76
78
  def initialize(options = {})
77
79
  super
@@ -82,6 +84,7 @@ module Chingu
82
84
  @max_read_per_update = options[:max_read_per_update] || 50000
83
85
 
84
86
  @socket = nil
87
+ @connected = false
85
88
  @latency = 0
86
89
  @packet_counter = 0
87
90
  @packet_buffer = NetworkServer::PacketBuffer.new
@@ -89,11 +92,35 @@ module Chingu
89
92
 
90
93
  #
91
94
  # Default network loop:
92
- # 1) read raw data from server with #handle_incoming_data
93
- # 2) #handle_incoming_data call #on_data(data)
94
- # 3) #on_data(data) will call #on_msgs(msg)
95
+ # 1) Try to complete outgoing connection if connect() has been called
96
+ # 2) read raw data from server with #handle_incoming_data
97
+ # 3) #handle_incoming_data call #on_data(data)
98
+ # 4) #on_data(data) will call #on_msgs(msg)
95
99
  #
96
100
  def update
101
+
102
+ if @socket and not @connected
103
+ begin
104
+ # Start/Check on our nonblocking tcp connection
105
+ @socket.connect_nonblock(@sockaddr)
106
+ rescue Errno::EINPROGRESS #rescue IO::WaitWritable
107
+ rescue Errno::EALREADY
108
+ if IO.select([@socket],nil,nil,0.1).nil?
109
+ @socket = nil
110
+ on_connection_refused
111
+ end
112
+ rescue Errno::EISCONN
113
+ @connected = true
114
+ on_connect
115
+ rescue Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EINVAL
116
+ @socket = nil
117
+ on_connection_refused
118
+ rescue Errno::ETIMEDOUT
119
+ @socket = nil
120
+ on_timeout
121
+ end
122
+ end
123
+
97
124
  handle_incoming_data
98
125
  super
99
126
  end
@@ -109,16 +136,9 @@ module Chingu
109
136
  @ip = ip if ip
110
137
  @port = port if port
111
138
 
112
- begin
113
- status = Timeout::timeout(@timeout) do
114
- @socket = TCPSocket.new(@ip, @port)
115
- on_connect
116
- end
117
- rescue Errno::ECONNREFUSED
118
- on_connection_refused
119
- rescue Timeout
120
- on_timeout
121
- end
139
+ # Set up our @socket, update() will handle the actual nonblocking connection
140
+ @socket = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
141
+ @sockaddr = Socket.sockaddr_in(@port, @ip)
122
142
 
123
143
  return self
124
144
  end
@@ -127,6 +147,7 @@ module Chingu
127
147
  # Called when connect() fails with connection refused (closed port)
128
148
  #
129
149
  def on_connection_refused
150
+ puts "[on_connection_refused() #{@ip}:#{@port}]" if @debug
130
151
  connect(@ip, @port)
131
152
  end
132
153
 
@@ -134,6 +155,7 @@ module Chingu
134
155
  # Called when connect() recieves no initial answer from server
135
156
  #
136
157
  def on_timeout
158
+ puts "[on_timeout() #{@ip}:#{@port}]" if @debug
137
159
  connect(@ip, @port)
138
160
  end
139
161
 
@@ -163,6 +185,8 @@ module Chingu
163
185
  packet, sender = @socket.recvfrom(amount)
164
186
  on_data(packet)
165
187
  rescue Errno::ECONNABORTED, Errno::ECONNRESET
188
+ @connected = false
189
+ @socket = nil
166
190
  on_disconnect
167
191
  end
168
192
  end
@@ -195,6 +219,8 @@ module Chingu
195
219
  @socket.write([data.length].pack(NetworkServer::PACKET_HEADER_FORMAT))
196
220
  @socket.write(data)
197
221
  rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, Errno::ENOTCONN
222
+ @connected = false
223
+ @socket = nil
198
224
  on_disconnect
199
225
  end
200
226
  end
@@ -111,8 +111,7 @@ module Chingu
111
111
  end
112
112
  end
113
113
 
114
- attr_reader :socket, :sockets, :ip, :port
115
-
114
+ attr_reader :socket, :sockets, :ip, :port, :max_connections
116
115
  alias_method :address, :ip
117
116
 
118
117
  def initialize(options = {})
@@ -121,10 +120,11 @@ module Chingu
121
120
  @ip = options[:ip] || "0.0.0.0"
122
121
  @port = options[:port] || DEFAULT_PORT
123
122
  @debug = options[:debug]
123
+ @max_read_per_update = options[:max_read_per_update] || 20000
124
+ @max_connections = options[:max_connections] || 256
125
+
124
126
  @socket = nil
125
127
  @sockets = []
126
- @max_read_per_update = options[:max_read_per_update] || 20000
127
-
128
128
  @packet_buffers = Hash.new
129
129
  end
130
130
 
@@ -197,9 +197,13 @@ module Chingu
197
197
  def handle_incoming_connections
198
198
  begin
199
199
  while socket = @socket.accept_nonblock
200
- @sockets << socket
201
- @packet_buffers[socket] = PacketBuffer.new
202
- on_connect(socket)
200
+ if @sockets.size < @max_connections
201
+ @sockets << socket
202
+ @packet_buffers[socket] = PacketBuffer.new
203
+ on_connect(socket)
204
+ else
205
+ socket.close
206
+ end
203
207
  end
204
208
  rescue IO::WaitReadable, Errno::EINTR
205
209
  end
@@ -215,7 +219,7 @@ module Chingu
215
219
  begin
216
220
  packet, sender = socket.recvfrom(max_size)
217
221
  on_data(socket, packet)
218
- rescue Errno::ECONNABORTED, Errno::ECONNRESET
222
+ rescue Errno::ECONNABORTED, Errno::ECONNRESET, IOError
219
223
  @packet_buffers[socket] = nil
220
224
 
221
225
  on_disconnect(socket)
@@ -271,11 +275,21 @@ module Chingu
271
275
  #
272
276
  def disconnect_client(socket)
273
277
  socket.close
278
+ @sockets.delete socket
279
+ @packet_buffers.delete socket
280
+ on_disconnect(socket)
281
+ rescue Errno::ENOTCONN
274
282
  end
275
283
 
276
284
  # Ensure that the buffer is cleared of data to write (call at the end of update or, at least after all sends).
277
285
  def flush
278
- @sockets.each {|s| s.flush }
286
+ @sockets.each do |socket|
287
+ begin
288
+ socket.flush
289
+ rescue IOError
290
+ disconnect_client(socket)
291
+ end
292
+ end
279
293
  end
280
294
 
281
295
  #
@@ -283,10 +297,12 @@ module Chingu
283
297
  #
284
298
  def stop
285
299
  return unless @socket
286
- begin
300
+
301
+ @sockets.each {|socket| disconnect_client(socket) }
302
+ @sockets = []
287
303
  @socket.close
304
+ @socket = nil
288
305
  rescue Errno::ENOTCONN
289
- end
290
306
  end
291
307
  alias close stop
292
308
 
@@ -71,7 +71,9 @@ module Chingu
71
71
  def to_s
72
72
  "#{self.class.to_s} @ #{x.to_i} / #{y.to_i} " <<
73
73
  "(#{width.to_i} x #{height.to_i}) - " <<
74
- " ratio: #{sprintf("%.2f",width/height)} scale: #{sprintf("%.2f", factor_x)}/#{sprintf("%.2f", factor_y)} angle: #{angle.to_i} zorder: #{zorder} alpha: #{alpha}"
74
+ "ratio: #{sprintf("%.2f",width.to_f/height.to_f)} " <<
75
+ "scale: #{sprintf("%.2f", factor_x)}/#{sprintf("%.2f", factor_y)} " <<
76
+ "angle: #{angle.to_i} zorder: #{zorder} alpha: #{alpha}"
75
77
  end
76
78
 
77
79
  def color=(color)
@@ -205,7 +207,7 @@ module Chingu
205
207
  # Disable automatic calling of draw and draw_trait each game loop
206
208
  #
207
209
  def hide!
208
- @parent.game_objects.hide_game_object(self) if @parent && @visible == true
210
+ @parent.game_objects.hide_game_object(self) if @parent && @visible
209
211
  @visible = false
210
212
  end
211
213
 
@@ -213,7 +215,7 @@ module Chingu
213
215
  # Enable automatic calling of draw and draw_trait each game loop
214
216
  #
215
217
  def show!
216
- @parent.game_objects.show_game_object(self) if @parent && @visible == false
218
+ @parent.game_objects.show_game_object(self) if @parent && !@visible
217
219
  @visible = true
218
220
  end
219
221
 
@@ -24,16 +24,16 @@ module Chingu
24
24
 
25
25
  #
26
26
  # A chingu trait providing timer-methods to its includer, examples:
27
- # during(300) { @color = Color.new(0xFFFFFFFF) } # forces @color to white ever update for 300 ms
28
- # after(400) { self.destroy! } # destroy object after 400 ms
29
- # between(1000,2000) { self.rotate(10) } # starting after 1 second, call rotate(10) each update during 1 second
27
+ # during(300) { @color = Color.new(0xFFFFFFFF) } # forces @color to white ever update for 300 ms
28
+ # after(400) { self.destroy! } # destroy object after 400 ms
29
+ # between(1000,2000) { self.rotate(10) } # starting after 1 second, call rotate(10) each update during 1 second
30
30
  #
31
31
  # All the above can be combined with a 'then { do_something }'. For example, a classic shmup damage effect:
32
- # during(100) { @color.alpha = 100 }.then { @color.alpha = 255 }
32
+ # during(100) { @color.alpha = 100 }.then { @color.alpha = 255 }
33
33
  #
34
34
  module Timer
35
35
 
36
- def setup_trait(options)
36
+ def setup_trait(options)
37
37
  #
38
38
  # Timers are saved as an array of arrays where each entry contains:
39
39
  # [name, start_time, end_time (or nil if one-shot), &block]
@@ -44,7 +44,7 @@ module Chingu
44
44
  end
45
45
 
46
46
  #
47
- # Executes block each update during 'time' milliseconds
47
+ # Executes block each update during 'time' milliseconds
48
48
  #
49
49
  def during(time, options = {}, &block)
50
50
  if options[:name]
@@ -59,7 +59,7 @@ module Chingu
59
59
  end
60
60
 
61
61
  #
62
- # Executes block after 'time' milliseconds
62
+ # Executes block after 'time' milliseconds
63
63
  #
64
64
  def after(time, options = {}, &block)
65
65
  if options[:name]
@@ -89,7 +89,7 @@ module Chingu
89
89
  end
90
90
 
91
91
  #
92
- # Executes block every 'delay' milliseconds
92
+ # Executes block every 'delay' milliseconds
93
93
  #
94
94
  def every(delay, options = {}, &block)
95
95
  if options[:name]
@@ -98,13 +98,17 @@ module Chingu
98
98
  end
99
99
 
100
100
  ms = Gosu::milliseconds()
101
- @_repeating_timers << [options[:name], ms + delay, delay, block]
101
+ @_repeating_timers << [options[:name], ms + delay, delay, options[:during] ? ms + options[:during] : nil, block]
102
+ if options[:during]
103
+ @_last_timer = [options[:name], nil, ms + options[:during]]
104
+ return self
105
+ end
102
106
  end
103
107
 
104
108
  #
105
- # Executes block after the last timer ends
109
+ # Executes block after the last timer ends
106
110
  # ...use one-shots start_time for our trailing "then".
107
- # ...use durable timers end_time for our trailing "then".
111
+ # ...use durable timers end_time for our trailing "then".
108
112
  #
109
113
  def then(&block)
110
114
  start_time = @_last_timer[2].nil? ? @_last_timer[1] : @_last_timer[2]
@@ -142,16 +146,20 @@ module Chingu
142
146
  ms = Gosu::milliseconds()
143
147
 
144
148
  @_timers.each do |name, start_time, end_time, block|
145
- block.call if ms > start_time && (end_time == nil || ms < end_time)
149
+ block.call if ms > start_time && (end_time == nil || ms < end_time)
146
150
  end
147
151
 
148
152
  index = 0
149
- @_repeating_timers.each do |name, start_time, delay, block|
153
+ @_repeating_timers.each do |name, start_time, delay, end_time, block|
150
154
  if ms > start_time
151
155
  block.call
152
- @_repeating_timers[index] = [name, ms + delay, delay, block]
156
+ @_repeating_timers[index] = [name, ms + delay, delay, end_time, block]
157
+ end
158
+ if end_time && ms > end_time
159
+ @_repeating_timers.delete_at index
160
+ else
161
+ index += 1
153
162
  end
154
- index += 1
155
163
  end
156
164
 
157
165
  # Remove one-shot timers (only a start_time, no end_time) and all timers which have expired
@@ -159,7 +159,7 @@ module Chingu
159
159
  a = @game_area
160
160
  %/
161
161
  Vieport
162
- Position: #{@x}, #{y}
162
+ Position: #{@x}, #{@y}
163
163
  Game area: #{a.x},#{a.y},#{a.width},#{a.height}"
164
164
  Target: #{@target_x}, #{@target_y}
165
165
  /
@@ -63,6 +63,13 @@ module Chingu
63
63
  @game.game_objects.draw
64
64
  end
65
65
 
66
+ it "should keep track of visible game objects with show!()" do
67
+ go = GameObject.create(:visible => false)
68
+ go.show!
69
+ go.should_receive(:draw)
70
+ @game.game_objects.draw
71
+ end
72
+
66
73
  it "should call draw() on all visible game objects" do
67
74
  GameObject.create.should_receive(:draw)
68
75
  GameObject.create(:visible => false).should_not_receive(:draw)
@@ -8,6 +8,7 @@ describe Chingu::Input do
8
8
  gosu_inputs = input_names.inject({}) {|hash, name| hash[name] = Gosu.const_get name; hash }
9
9
 
10
10
  gosu_inputs.each_value do |code|
11
+ next if code==0 # todo, check into this, spooner? ;)
11
12
  symbols = described_class::CONSTANT_TO_SYMBOL[code]
12
13
  symbols.should_not be_empty
13
14
  symbols.each {|s| s.should be_kind_of Symbol }
@@ -22,6 +22,15 @@ module Chingu
22
22
  @server.stop
23
23
  end
24
24
 
25
+ it "client should timeout when connecting to blackhole ip" do
26
+ @client = Chingu::GameStates::NetworkClient.new(:ip => "1.2.3.4", :port => 1234, :debug => true)
27
+ @client.connect
28
+
29
+ #@client.should_receive(:on_timeout) ## gives on_connection_refused instead, kind of ok.
30
+ @client.should_receive(:on_connection_refused)
31
+ @client.update while @client.socket
32
+ end
33
+
25
34
  it "should call on_start_error() if failing" do
26
35
  @server = described_class.new(:ip => "1.2.3.999", :port => 12345678) # crazy ip:port
27
36
  @server.should_receive(:on_start_error)
@@ -38,6 +47,8 @@ module Chingu
38
47
  @client.should_receive(:on_connect)
39
48
  @server.start
40
49
  @client.connect
50
+
51
+ @client.update until @client.connected?
41
52
  @server.update
42
53
 
43
54
  @client.stop
@@ -51,6 +62,7 @@ module Chingu
51
62
  @client = described_class.new(:ip => "127.0.0.1", :port => 55421) # closed we assume
52
63
  @client.should_receive(:on_connection_refused)
53
64
  @client.connect
65
+ 5.times { @client.update }
54
66
  end
55
67
  end
56
68
 
@@ -59,6 +71,8 @@ module Chingu
59
71
  @server = Chingu::GameStates::NetworkServer.new(:port => 9999).start
60
72
  @client = Chingu::GameStates::NetworkClient.new(:ip => "127.0.0.1", :port => 9999).connect
61
73
  @client2 = Chingu::GameStates::NetworkClient.new(:ip => "127.0.0.1", :port => 9999).connect
74
+ @client.update until @client.connected?
75
+ @client2.update until @client2.connected?
62
76
  end
63
77
 
64
78
  after :each do
@@ -73,7 +87,7 @@ module Chingu
73
87
  data.each {|packet| @server.should_receive(:on_msg).with(an_instance_of(TCPSocket), packet) }
74
88
  data.each {|packet| @client.send_msg(packet) }
75
89
 
76
- @server.update
90
+ 5.times { @server.update }
77
91
  end
78
92
  end
79
93
  end
@@ -85,7 +99,7 @@ module Chingu
85
99
  @server.update # Accept the client before sending, so we know of its socket.
86
100
  data.each { |packet| @server.send_msg(@server.sockets[0], packet) }
87
101
 
88
- @client.update
102
+ 5.times { @client.update }
89
103
  end
90
104
  end
91
105
  end
@@ -102,8 +116,10 @@ module Chingu
102
116
 
103
117
  data.each {|packet| @server.broadcast_msg(packet) }
104
118
 
105
- @client.update
106
- @client2.update
119
+ 5.times do
120
+ @client.update
121
+ @client2.update
122
+ end
107
123
  end
108
124
  end
109
125
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chingu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9rc4
4
+ version: 0.9rc5
5
5
  prerelease: 3
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-03-07 00:00:00.000000000 +01:00
12
+ date: 2011-05-28 00:00:00.000000000 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: gosu
17
- requirement: &25121820 !ruby/object:Gem::Requirement
17
+ requirement: &24886884 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 0.7.27.1
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *25121820
25
+ version_requirements: *24886884
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rspec
28
- requirement: &25121472 !ruby/object:Gem::Requirement
28
+ requirement: &24886572 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 2.1.0
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *25121472
36
+ version_requirements: *24886572
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: watchr
39
- requirement: &25120920 !ruby/object:Gem::Requirement
39
+ requirement: &24886188 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *25120920
47
+ version_requirements: *24886188
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: rcov
50
- requirement: &25120464 !ruby/object:Gem::Requirement
50
+ requirement: &24885792 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,7 +55,7 @@ dependencies:
55
55
  version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *25120464
58
+ version_requirements: *24885792
59
59
  description: OpenGL accelerated 2D game framework for Ruby. Builds on Gosu (Ruby/C++)
60
60
  which provides all the core functionality. Chingu adds simple yet powerful game
61
61
  states, prettier input handling, deployment safe asset-handling, a basic re-usable