chingu 0.9rc4 → 0.9rc5

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.
@@ -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