pax_global_header00006660000000000000000000000064136761340740014525gustar00rootroot0000000000000052 comment=938c0b66f1c315c9a58ddeb88dff59516a965feb ruby-rubame-0.0.3~git20131224.f3c78ba/000077500000000000000000000000001367613407400166345ustar00rootroot00000000000000ruby-rubame-0.0.3~git20131224.f3c78ba/.gitignore000066400000000000000000000004601367613407400206240ustar00rootroot00000000000000# Editors # ########### *.sublime-workspace *.*~ *.texpadtmp # Citadels # ############ *.log *.dat # Project specific # #################### *.gem test_fiddle.rb Gemfile.lock # OS generated files # ###################### .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes Icon? ehthumbs.db Thumbs.db ruby-rubame-0.0.3~git20131224.f3c78ba/Gemfile000066400000000000000000000000461367613407400201270ustar00rootroot00000000000000source "http://rubygems.org" gemspec ruby-rubame-0.0.3~git20131224.f3c78ba/README.md000066400000000000000000000101561367613407400201160ustar00rootroot00000000000000# Rubame Rubame is a simple Ruby websocket game server. I was originally using EventMachine for a private project, but it became apparent that EM may not be the most suitable tool. EM is designed (as I understand it) for servers that will handle lots of low duration connections, whereas my project will involve only a small handful (between 1 and 20, likely) that are long lasting. However, the primary concern I had was how EM takes over the thread. In order to have game logic run, it seemed the options were to create a new thread, or use periodic timers to have a game loop called. Neither seemed ideal, and I wanted to have more control in order to better manage cpu usage. Rubame allows me to run the network loop when it suits me, allowing the game logic/engine to be the primary tool. Rubame makes use of [WebSocket Ruby](https://github.com/imanel/websocket-ruby) to handle the websocket protocol, and the standard ruby sockets libraries for the actual network connections. I am by no means a Ruby expert, so I expect there to be many ways I could improve this. I also expect there are ways I have done this that will make someone proficient in Ruby cringe. If you have such comments, please [email me](mailto:me@marksaward.com). I also expect to improve the code as my project progresses, and my needs grow. However, I will keep improvements to only be those that are of general use, and not specific to my project. ## Example Use ```ruby require 'rubame' server = Rubame::Server.new("0.0.0.0", 25252) while true server.run do |client| client.onopen do puts "Server reports: client open" end client.onmessage do |mess| puts "Server reports: message received: #{mess}" client.send "You sent me #{mess}" end client.onclose do puts "Server reports: client closed" end end end ``` A useful Chrome plugin to help test connections is this [Simple Websocket Client](https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo). Each time the loop is run, it checks with ruby IO to see if any sockets have any new data. It then reads up to (currently) 2000 bytes from that socket. If there is more data than that waiting, it will have to wait until the next time the loop is called. Once enough data has been received for a complete frame, that frame will be read and returned as a message, via client.onmessage as seen in the example. ## Possible Future Features * Binary data - Currently, Rubame only deals with text network traffic. This is not ideal, and so I hope to add the ability to send binary data, along with examples here showing how to communicate with a javascript client. * Lazy send - Currently, Rubame sends a message as soon as the message is requested to be sent. Sometimes, a game may want to send some traffic whenever it is convenient, and some traffic immediately. I may add the ability to lazy send data. __Please Note:__ The lazy send code currently is broken, so do not use it! ## RSpec The RSpec is quite incomplete for now. I expect to add more tests later. ## License (The MIT License) Copyright © 2013 Mark Saward Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ruby-rubame-0.0.3~git20131224.f3c78ba/Rakefile000066400000000000000000000003321367613407400202770ustar00rootroot00000000000000require 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new do |t| t.rspec_opts = ["-c", "-f progress"] t.pattern = 'spec/**/*_spec.rb' end task :default => :spec ruby-rubame-0.0.3~git20131224.f3c78ba/lib/000077500000000000000000000000001367613407400174025ustar00rootroot00000000000000ruby-rubame-0.0.3~git20131224.f3c78ba/lib/rubame.rb000066400000000000000000000113301367613407400212000ustar00rootroot00000000000000require 'websocket' require 'socket' require 'fiber' module Rubame class Server def initialize(host, port) Socket.do_not_reverse_lookup @hostname = host @port = port @reading = [] @writing = [] @clients = {} # Socket as key, and Client as value @socket = TCPServer.new(@hostname, @port) @reading.push @socket end def accept socket = @socket.accept_nonblock @reading.push socket handshake = WebSocket::Handshake::Server.new client = Rubame::Client.new(socket, handshake, self) while line = socket.gets client.handshake << line break if client.handshake.finished? end if client.handshake.valid? @clients[socket] = client client.write handshake.to_s client.opened = true return client else close(client) end return nil end def read(client) pairs = client.socket.recvfrom(2000) messages = [] if pairs[0].length == 0 close(client) else client.frame << pairs[0] while f = client.frame.next if (f.type == :close) close(client) return messages else messages.push f end end end return messages end def close(client) @reading.delete client.socket @clients.delete client.socket begin client.socket.close rescue end client.closed = true end def run(time = 0, &blk) readable, writable = IO.select(@reading, @writing, nil, 0) if readable readable.each do |socket| client = @clients[socket] if socket == @socket client = accept else msg = read(client) client.messaged = msg end blk.call(client) if client and blk end end # Check for lazy send items timer_start = Time.now time_passed = 0 begin @clients.each do |s, c| c.send_some_lazy(5) end time_passed = Time.now - timer_start end while time_passed < time end def stop @socket.close end end class Client attr_accessor :socket, :handshake, :frame, :opened, :messaged, :closed def initialize(socket, handshake, server) @socket = socket @handshake = handshake @frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version) @opened = false @messaged = [] @lazy_queue = [] @lazy_current_queue = nil @closed = false @server = server end def write(data) @socket.write data end def send(data) frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => data, :type => :text) begin @socket.write frame @socket.flush rescue @server.close(self) unless @closed end end def lazy_send(data) @lazy_queue.push data end def get_lazy_fiber # Create the fiber if needed if @lazy_fiber == nil or !@lazy_fiber.alive? @lazy_fiber = Fiber.new do @lazy_current_queue.each do |data| send(data) Fiber.yield unless @lazy_current_queue[-1] == data end end end return @lazy_fiber end def send_some_lazy(count) # To save on cpu cycles, we don't want to be chopping and changing arrays, which could get quite large. Instead, # we iterate over an array which we are sure won't change out from underneath us. unless @lazy_current_queue @lazy_current_queue = @lazy_queue @lazy_queue = [] end completed = 0 begin get_lazy_fiber.resume completed += 1 end while (@lazy_queue.count > 0 or @lazy_current_queue.count > 0) and completed < count end def onopen(&blk) if @opened begin blk.call ensure @opened = false end end end def onmessage(&blk) if @messaged.size > 0 begin @messaged.each do |x| blk.call(x.to_s) end ensure @messaged = [] end end end def onclose(&blk) if @closed begin blk.call ensure end end end end line = 0 end if __FILE__==$0 server = Rubame::Server.new("0.0.0.0", 25222) while (!$quit) server.run do |client| client.onopen do puts "Server reports: client open" end client.onmessage do |mess| puts "Server reports: message received: #{mess}" end client.onclose do puts "Server reports: client closed" end end end end ruby-rubame-0.0.3~git20131224.f3c78ba/rubame.gemspec000066400000000000000000000006161367613407400214570ustar00rootroot00000000000000Gem::Specification.new do |s| s.name = 'rubame' s.version = '0.0.3' s.date = '2013-01-07' s.summary = "Rubame" s.description = "Ruby Websocket Game Server" s.authors = ["Mark Saward"] s.email = 'me@marksaward.com' s.files = ["lib/rubame.rb"] s.homepage = 'http://github.com/saward/Rubame' s.add_dependency 'websocket', '~> 1.0' end ruby-rubame-0.0.3~git20131224.f3c78ba/spec/000077500000000000000000000000001367613407400175665ustar00rootroot00000000000000ruby-rubame-0.0.3~git20131224.f3c78ba/spec/rubame_spec.rb000066400000000000000000000075171367613407400224120ustar00rootroot00000000000000require 'spec_helper' describe Rubame::Server do it 'should create a new server' do server = Rubame::Server.new("0.0.0.0", 29929) server.class.should == Rubame::Server server.stop end it 'should receive a new client connecting' do server = Rubame::Server.new("0.0.0.0", 29929) client = TCPSocket.new 'localhost', 29929 handshake = WebSocket::Handshake::Client.new(:url => 'ws://127.0.0.1:29929') client.write handshake.to_s connected = false server.run do |client| client.onopen do connected = true end end connected.should == true server.stop end it 'should send handshake replies to 10 clients' do server = Rubame::Server.new("0.0.0.0", 29929) clients = {} finished = false Thread.new do while server server.run end end sleep(0.3) 10.times do client = TCPSocket.new 'localhost', 29929 handshake = WebSocket::Handshake::Client.new(:url => 'ws://127.0.0.1:29929') clients[client] = handshake client.write handshake.to_s while line = client.gets handshake << line break if handshake.finished? end handshake.finished?.should == true end clients.each do |s, h| s.close end server.stop server = nil finished = true end it 'should be able to send a message to a client' do server = Rubame::Server.new("0.0.0.0", 29929) finished = false Thread.new do while server server.run do |client| client.onopen do client.send "Tester" end end end end sleep(0.3) client = TCPSocket.new 'localhost', 29929 handshake = WebSocket::Handshake::Client.new(:url => 'ws://127.0.0.1:29929') client.write handshake.to_s while line = client.gets handshake << line break if handshake.finished? end handshake.finished?.should == true frame = WebSocket::Frame::Incoming::Client.new(:version => handshake) waiting = true time_started = Time.now time_passed = 0.0 while waiting r, w = IO.select([client], [], nil, 0) time_passed = Time.now - time_started if r r.each do |s| pairs = client.recvfrom(20000) frame << pairs[0] # puts frame.next waiting = false end end waiting = false if time_passed > 4 end (/Tester/ =~ frame.to_s).class.should eq(Fixnum) client.close server.stop server = nil finished = true end it 'should send lazy items after immediate ones' do server = Rubame::Server.new("0.0.0.0", 29929) finished = false Thread.new do while server server.run do |client| client.onopen do (0..3).each do |x| client.lazy_send("#{x}") end client.send("4") end end end end sleep(0.3) client = TCPSocket.new 'localhost', 29929 handshake = WebSocket::Handshake::Client.new(:url => 'ws://127.0.0.1:29929') client.write handshake.to_s while line = client.gets handshake << line break if handshake.finished? end handshake.finished?.should == true frame = WebSocket::Frame::Incoming::Client.new(:version => handshake) waiting = true time_started = Time.now time_passed = 0.0 while waiting r, w = IO.select([client], [], nil, 0) time_passed = Time.now - time_started if r r.each do |s| pairs = client.recvfrom(20000) frame << pairs[0] # puts frame.next waiting = false if (/.*4.*0.*1.*2.*3.*/ =~ frame.to_s) end end waiting = false if time_passed > 4 end (/.*4.*0.*1.*2.*3.*/ =~ frame.to_s).class.should eq(Fixnum) client.close server.stop server = nil finished = true end end ruby-rubame-0.0.3~git20131224.f3c78ba/spec/spec_helper.rb000066400000000000000000000000421367613407400224000ustar00rootroot00000000000000require 'rspec' require 'rubame'