teeworlds 1.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 61cf94210fcf13237887b3c092453ff844712ad7
4
+ data.tar.gz: 73a08040c3a9d38aaf5e7ddbd01442a6cb3587bf
5
+ SHA512:
6
+ metadata.gz: a52ce1dc1bb6b94bf1ec0e33d0f54dc87c30cd1a40fccf4dedca6bd45f96885fbd490a8cea938cb3f46751569105b5ce512c80bd26598c3915345a4112bbf232
7
+ data.tar.gz: 7ce80764ea4bcbb11cfcfab309edb4263367b6438cd371b43d419e53832b08a41c019dc46ffc815aef5172e33834213d37cf3cb72e9c89d0cc54ba2f8d0aa252
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ m.rb
3
+ *.swp
4
+ *~
5
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in teeworlds.gemspec
4
+ gemspec
@@ -0,0 +1,44 @@
1
+ # Teeworlds
2
+
3
+ Teeworlds is a multiplayer shooter. This gem allows you to fetch all available game servers and view current status of each.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'teeworlds'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install teeworlds
18
+
19
+ ## Usage
20
+
21
+ ms = Teeworlds::MasterServer.new
22
+
23
+ ms.servers.each do |tw|
24
+ puts "#{tw.server}: #{tw.port}"
25
+ end
26
+
27
+ s = ms.servers.first
28
+ s.connect
29
+
30
+ puts "Server name: #{s.name}, map: #{s.map}, type: #{s.gametype}"
31
+ puts "Num clients: #{s.num_clients}"
32
+ puts "Max clients: #{s.max_clients}"
33
+ puts "Num player: #{s.num_players}"
34
+ puts "Max player: #{s.max_players}"
35
+
36
+ s.players.each { |player| puts player }
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create new Pull Request
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ task :default => :spec
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.rspec_opts = %w(-fp --color)
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ module Teeworlds
5
+ end
6
+
7
+ require 'teeworlds/version'
8
+ require 'teeworlds/masterserver'
9
+ require 'teeworlds/server'
@@ -0,0 +1,33 @@
1
+ module Teeworlds
2
+ class MasterServer
3
+ attr_reader :servers
4
+
5
+ def initialize
6
+ @servers = []
7
+
8
+ 1.upto(4) do |num|
9
+ connect_and_request("master#{num}.teeworlds.com", 8300)
10
+ end
11
+ end
12
+
13
+ private
14
+ def connect_and_request(server, port)
15
+ udp = UDPSocket.new
16
+ udp.connect(server, port)
17
+ udp.send "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xff\x72\x65\x71\x32", 0
18
+
19
+ Timeout.timeout(1) do
20
+ loop { parse_servers udp.recvfrom(1400).first }
21
+ end
22
+ rescue SocketError, Errno::ECONNREFUSED, Timeout::Error
23
+ udp.close
24
+ end
25
+
26
+ def parse_servers(servers_raw)
27
+ servers_raw.bytes.to_a[14..-1].each_slice(18) do |s|
28
+ sp = s.pack('C*').unpack('@12C4n')
29
+ @servers.push Teeworlds::Server.new(server: sp[0..3].join('.'), port: sp.last)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ module Teeworlds
2
+ class Server
3
+ METHODS = [:version, :name, :map, :gametype, :flags, :num_players, :max_players, :num_clients, :max_clients]
4
+ attr_reader :server, :port, :players
5
+
6
+ def initialize(arg)
7
+ @server = arg[:server]
8
+ @port = arg[:port] || 8303
9
+ @players = []
10
+ end
11
+
12
+ def connect
13
+ Timeout.timeout(1) do
14
+ udp = UDPSocket.new
15
+ udp.connect(@server, @port)
16
+ udp.send("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x67\x69\x65\x33\x00", 0)
17
+ create_methods udp.recvfrom(1024).first.split("\x00")[1..-1]
18
+ end
19
+ end
20
+
21
+ private
22
+ def create_methods(recv)
23
+ self.class.class_eval do
24
+ (0...METHODS.size).each do |num|
25
+ define_method(METHODS[num]) { recv[num] }
26
+ end
27
+ end
28
+
29
+ parse_players(recv)
30
+ end
31
+
32
+ def parse_players(recv)
33
+ recv[9..-1].each_slice(5) do |player|
34
+ @players.push(
35
+ name: player[0],
36
+ clan: (player[1].empty?) ? nil : player[1],
37
+ country: (player[2] == '-1') ? nil : player[2].to_i,
38
+ score: player[3].to_i,
39
+ playing?: (player[4] == '1') ? true : false
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Teeworlds
2
+ VERSION = '1.0.2'
3
+ end
@@ -0,0 +1,182 @@
1
+ require 'teeworlds'
2
+
3
+ describe Teeworlds::MasterServer do
4
+ before do
5
+ master_5_servers = [File.open('spec/fixtures/master_5_servers', 'r:ASCII-8BIT').read]
6
+ @socket_mock = mock
7
+ @socket_mock.should_receive(:connect).exactly(4).times
8
+ @socket_mock.should_receive(:send).exactly(4).times
9
+ @socket_mock.should_receive(:recvfrom).and_return(master_5_servers)
10
+ @socket_mock.should_receive(:recvfrom).at_least(3).times.and_return(['.' * 14])
11
+ @socket_mock.should_receive(:close).exactly(4).times
12
+ UDPSocket.stub(new: @socket_mock)
13
+ end
14
+
15
+ describe '.new' do
16
+ it 'should connect to masterservers 1-4' do
17
+ Teeworlds::MasterServer.new
18
+ end
19
+ end
20
+
21
+ describe '#servers' do
22
+ it 'should be an array of Teeworlds::Server' do
23
+ Teeworlds::MasterServer.new.servers.each do |server|
24
+ server.should be_a(Teeworlds::Server)
25
+ end
26
+ end
27
+
28
+ it 'should return 5 servers' do
29
+ ms = Teeworlds::MasterServer.new
30
+
31
+ [ms.servers[0].server, ms.servers[0].port].should == ['88.198.182.255', 9123]
32
+ [ms.servers[1].server, ms.servers[1].port].should == ['109.73.50.121', 9016]
33
+ [ms.servers[2].server, ms.servers[2].port].should == ['46.38.237.106', 8308]
34
+ [ms.servers[3].server, ms.servers[3].port].should == ['188.233.98.250', 8303]
35
+ [ms.servers[4].server, ms.servers[4].port].should == ['217.29.118.189', 8202]
36
+ end
37
+ end
38
+ end
39
+
40
+ describe Teeworlds::Server do
41
+ context 'when connection failed' do
42
+ it 'raises Errno::ECONNREFUSED if could not connect to server' do
43
+ expect { Teeworlds::Server.new(server: '127.0.0.1', port: 6666).connect }.to raise_error(Errno::ECONNREFUSED)
44
+ end
45
+ end
46
+
47
+ context 'when connection succeeded' do
48
+ before do
49
+ server_3_players = [File.open('spec/fixtures/server_3_players', 'r:ASCII-8BIT').read]
50
+ @socket_mock = mock
51
+ @socket_mock.should_receive(:send).with("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x67\x69\x65\x33\x00", 0)
52
+ @socket_mock.should_receive(:recvfrom).and_return(server_3_players)
53
+ UDPSocket.stub(new: @socket_mock)
54
+ #UDPSocket.should_receive(:new).and_return(@socket_mock)
55
+ end
56
+
57
+ let(:tw_server) { Teeworlds::Server.new(server: '127.0.0.1', port: 8500) }
58
+ let(:tw_server_no_port) { Teeworlds::Server.new(server: '127.0.0.1') }
59
+
60
+ describe '.new' do
61
+ it 'should connect to requested server and port' do
62
+ @socket_mock.should_receive(:connect).with('127.0.0.1', 8500)
63
+ tw_server.connect
64
+ end
65
+
66
+ it "should connect to default port if none is given" do
67
+ @socket_mock.should_receive(:connect).with('127.0.0.1', 8303)
68
+ tw_server_no_port.connect
69
+ end
70
+ end
71
+
72
+ describe 'instance method' do
73
+ before do
74
+ @socket_mock.should_receive(:connect).with('127.0.0.1', 8500)
75
+ tw_server.connect
76
+ end
77
+
78
+ subject { tw_server }
79
+
80
+ describe '#version' do
81
+ it { should respond_to(:version) }
82
+ it 'equals "0.6 trunk, 1.14a"' do
83
+ subject.version.should eql '0.6 trunk, 1.14a'
84
+ end
85
+ end
86
+
87
+ describe '#name' do
88
+ it { should respond_to(:name) }
89
+ it 'equals "FS Server"' do
90
+ subject.name.should eql 'FS Server'
91
+ end
92
+ end
93
+
94
+ describe '#map' do
95
+ it { should respond_to(:map) }
96
+ it 'equals "xyz_ddrace2"' do
97
+ subject.map.should eql 'xyz_ddrace2'
98
+ end
99
+ end
100
+
101
+ describe '#gametype' do
102
+ it { should respond_to(:gametype) }
103
+ it 'equals "DDRace"' do
104
+ subject.gametype.should eql 'DDRace'
105
+ end
106
+ end
107
+
108
+ describe '#flags' do
109
+ it { should respond_to(:flags) }
110
+ it 'equals "0"' do
111
+ subject.flags.should eql '0'
112
+ end
113
+ end
114
+
115
+ describe '#num_players' do
116
+ it { should respond_to(:num_players) }
117
+ it 'equals "3"' do
118
+ subject.num_players.should eql '3'
119
+ end
120
+ end
121
+
122
+ describe '#max_players' do
123
+ it { should respond_to(:max_players) }
124
+ it 'equals "15"' do
125
+ subject.max_players.should eql '15'
126
+ end
127
+ end
128
+
129
+ describe '#num_clients' do
130
+ it { should respond_to(:num_clients) }
131
+ it 'equals "3"' do
132
+ subject.num_clients.should eql '3'
133
+ end
134
+ end
135
+
136
+ describe '#max_clients' do
137
+ it { should respond_to(:max_clients) }
138
+ it 'equals "15"' do
139
+ subject.max_clients.should eql '15'
140
+ end
141
+ end
142
+
143
+ describe '#players' do
144
+ it { should respond_to(:players) }
145
+
146
+ it 'returns an array' do
147
+ subject.players.should be_an(Array)
148
+ end
149
+
150
+ it 'has 3 elements' do
151
+ subject.players.should have(3).items
152
+ end
153
+
154
+ it 'is an array of hashes with name, clan, country, score and playing keys' do
155
+ subject.players[0].should == {
156
+ name: 'Jules',
157
+ clan: 'Gangsta',
158
+ country: 616,
159
+ score: -9999,
160
+ playing?: true
161
+ }
162
+
163
+ subject.players[1].should == {
164
+ name: 'Vincent',
165
+ clan: 'Gangsta',
166
+ country: 276,
167
+ score: -9999,
168
+ playing?: false
169
+ }
170
+
171
+ subject.players[2].should == {
172
+ name: 'Butch',
173
+ clan: 'Boxer',
174
+ country: nil,
175
+ score: -9999,
176
+ playing?: true
177
+ }
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'teeworlds/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "teeworlds"
8
+ spec.version = Teeworlds::VERSION
9
+ spec.authors = ["Maciej Borosiewicz"]
10
+ spec.email = ["m.borosiewicz@gmail.com"]
11
+ spec.description = %q{Teeworlds is a multiplayer shooter. This gem allows you to fetch all available game servers and view current status of each.}
12
+ spec.summary = %q{Classes to parse Teeworlds servers.}
13
+ spec.homepage = "https://github.com/xmbr/teeworlds"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "rspec", "~> 2.13"
21
+ spec.add_development_dependency "rake"
22
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: teeworlds
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Maciej Borosiewicz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Teeworlds is a multiplayer shooter. This gem allows you to fetch all
42
+ available game servers and view current status of each.
43
+ email:
44
+ - m.borosiewicz@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - .rspec
51
+ - Gemfile
52
+ - README.md
53
+ - Rakefile
54
+ - lib/teeworlds.rb
55
+ - lib/teeworlds/masterserver.rb
56
+ - lib/teeworlds/server.rb
57
+ - lib/teeworlds/version.rb
58
+ - spec/fixtures/master_5_servers
59
+ - spec/fixtures/server_3_players
60
+ - spec/teeworlds_spec.rb
61
+ - teeworlds.gemspec
62
+ homepage: https://github.com/xmbr/teeworlds
63
+ licenses: []
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.0.3
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Classes to parse Teeworlds servers.
85
+ test_files:
86
+ - spec/fixtures/master_5_servers
87
+ - spec/fixtures/server_3_players
88
+ - spec/teeworlds_spec.rb