maniaplanet-rpc 0.1.0 → 0.2.0
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.
- data/lib/maniaplanet_rpc/maniaplanet_client.rb +130 -102
- metadata +3 -3
@@ -1,137 +1,165 @@
|
|
1
1
|
require 'xmlrpc/client'
|
2
2
|
require 'thread'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
4
|
+
module ManiaRPC
|
5
|
+
class ManiaConnection
|
6
|
+
|
7
|
+
include XMLRPC::ParserWriterChooseMixin
|
8
|
+
|
9
|
+
def initialize(ip, port, callback)
|
10
|
+
@callback = callback
|
11
|
+
@request_handle = 0x80000000
|
12
|
+
@ip = ip
|
13
|
+
@port = port
|
14
|
+
@send_queue = Queue.new
|
15
|
+
begin
|
16
|
+
@socket = TCPSocket.new ip, port
|
17
|
+
rescue
|
18
|
+
puts "Failed to establish a connection, is the ManiaPlanet server really running?"
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
@protocol = 0
|
22
|
+
handshake
|
23
|
+
end
|
18
24
|
|
19
|
-
|
20
|
-
|
25
|
+
def handshake
|
26
|
+
header = @socket.recv(4).unpack "Vsize"
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
|
28
|
+
if header[0] > 64
|
29
|
+
raise Exception, "Wrong low-level protocol header!"
|
30
|
+
end
|
25
31
|
|
26
|
-
|
32
|
+
handshake = @socket.recv header[0]
|
27
33
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
if handshake == "GBXRemote 1"
|
35
|
+
@protocol = 1
|
36
|
+
elsif handshake == "GBXRemote 2"
|
37
|
+
@protocol = 2
|
38
|
+
else
|
39
|
+
raise Exception, "Unknown protocol version!"
|
40
|
+
end
|
34
41
|
end
|
35
|
-
end
|
36
42
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
43
|
+
def start
|
44
|
+
loop do
|
45
|
+
sleep(1.0/24.0)
|
46
|
+
tick
|
47
|
+
end
|
41
48
|
end
|
42
|
-
end
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
50
|
+
def call(method, *args)
|
51
|
+
@request_handle = @request_handle + 1
|
52
|
+
@send_queue.push [@request_handle, create().methodCall(method, *args)]
|
53
|
+
@request_handle - 0x80000000
|
54
|
+
end
|
49
55
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
def tick
|
57
|
+
write
|
58
|
+
read
|
59
|
+
end
|
54
60
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
def read
|
62
|
+
if IO.select([@socket], nil, nil, 0) != nil
|
63
|
+
if @protocol == 1
|
64
|
+
content = @socket.recv 4
|
65
|
+
if content.bytesize == 0
|
66
|
+
raise Exception, "Cannot read size!"
|
67
|
+
end
|
68
|
+
result = content.unpack "Vsize"
|
69
|
+
size = result[0]
|
70
|
+
receive_handle = @request_handle
|
71
|
+
else
|
72
|
+
content = @socket.recv 8
|
73
|
+
if content.bytesize == 0
|
74
|
+
raise Exception, "Cannot read size/handle!"
|
75
|
+
end
|
76
|
+
result = content.unpack "Vsize/Vhandle"
|
77
|
+
size = result[0]
|
78
|
+
receive_handle = result[1]
|
61
79
|
end
|
62
|
-
result = content.unpack "Vsize"
|
63
|
-
size = result[0]
|
64
|
-
receive_handle = @request_handle
|
65
|
-
else
|
66
|
-
content = @socket.recv 8
|
67
|
-
if content.bytesize == 0
|
68
|
-
raise Exception, "Cannot read size/handle!"
|
69
|
-
end
|
70
|
-
result = content.unpack "Vsize/Vhandle"
|
71
|
-
size = result[0]
|
72
|
-
receive_handle = result[1]
|
73
|
-
end
|
74
80
|
|
75
|
-
|
76
|
-
|
77
|
-
|
81
|
+
if receive_handle == 0 || size == 0
|
82
|
+
raise Exception, "Connection interrupted!"
|
83
|
+
end
|
78
84
|
|
79
|
-
|
80
|
-
|
81
|
-
|
85
|
+
if size > 4096 * 1024
|
86
|
+
raise Exception, "Response too large!"
|
87
|
+
end
|
82
88
|
|
83
|
-
|
84
|
-
|
89
|
+
response = ""
|
90
|
+
response_length = 0
|
85
91
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
while response_length < size
|
93
|
+
response << @socket.recv(size - response_length)
|
94
|
+
response_length = response.bytesize
|
95
|
+
end
|
90
96
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
97
|
+
begin # Response
|
98
|
+
response = parser().parseMethodResponse(response)
|
99
|
+
@callback.response_map[receive_handle].call response
|
100
|
+
rescue Exception # Callback
|
101
|
+
response = parser().parseMethodCall(response)
|
102
|
+
@callback.parse_callback response
|
103
|
+
end
|
95
104
|
end
|
96
105
|
end
|
97
|
-
end
|
98
106
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
107
|
+
def write
|
108
|
+
while @send_queue.length > 0
|
109
|
+
request = @send_queue.pop
|
110
|
+
if @protocol == 1
|
111
|
+
bytes = [request[1].bytesize, request[1]].pack("Va*")
|
112
|
+
else
|
113
|
+
bytes = [request[1].bytesize, request[0], request[1]].pack("VVa*")
|
114
|
+
end
|
115
|
+
@socket.write bytes
|
106
116
|
end
|
107
|
-
@socket.write bytes
|
108
117
|
end
|
118
|
+
|
109
119
|
end
|
110
120
|
|
111
|
-
|
121
|
+
##
|
122
|
+
# This class provides the interface for a ManiaPlanet Server by sending/receiving requests as well as callbacks.
|
123
|
+
class ManiaClient
|
112
124
|
|
113
|
-
|
125
|
+
attr_reader :connection
|
126
|
+
attr_accessor :response_map
|
114
127
|
|
115
|
-
|
116
|
-
|
128
|
+
def initialize(ip, port)
|
129
|
+
@response_map = {}
|
130
|
+
@callback_map = {}
|
131
|
+
@connection = ManiaConnection.new ip, port, self
|
132
|
+
Thread.new do
|
133
|
+
@connection.start
|
134
|
+
end
|
135
|
+
end
|
117
136
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
Thread.new do
|
122
|
-
@connection.start
|
137
|
+
def parse_callback(message)
|
138
|
+
@callback_map[message.first].call message
|
139
|
+
@callback_map[:all].call message
|
123
140
|
end
|
124
|
-
end
|
125
141
|
|
126
|
-
|
127
|
-
#
|
128
|
-
|
142
|
+
##
|
143
|
+
# Call this with a block to handle a callback with the given name.
|
144
|
+
def on(what, &block)
|
145
|
+
@callback_map[what] = block
|
146
|
+
end
|
129
147
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
148
|
+
##
|
149
|
+
# Call this with a block to receive all callbacks. Can be used in conjunction with the on method.
|
150
|
+
def all(&block)
|
151
|
+
@callback_map[:all] = block
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# Calls an RPC method with the given name and arguments, optionally handling the result within the passed block.
|
156
|
+
# Note: The response is asynchronous.
|
157
|
+
def call(method, *args, &block)
|
158
|
+
if block_given?
|
159
|
+
@response_map[@connection.call(method, *args)] = block
|
160
|
+
else
|
161
|
+
@response_map[@connection.call(method, *args)] = proc {}
|
162
|
+
end
|
135
163
|
end
|
136
164
|
end
|
137
165
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maniaplanet-rpc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-12 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: maniaplanet-rpc is a library for interfacing with maniaplanet servers
|
15
|
-
using
|
15
|
+
using its custom variant of xml-rpc
|
16
16
|
email: johan.f.ljungberg@gmail.com
|
17
17
|
executables: []
|
18
18
|
extensions: []
|