smartfox_jruby 0.2-java
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/Doubleshot +32 -0
- data/lib/smartfox_jruby/common.rb +152 -0
- data/lib/smartfox_jruby/sfs_adapter.rb +172 -0
- data/lib/smartfox_jruby/sfs_runner.rb +123 -0
- data/lib/smartfox_jruby/sfs_worker.rb +256 -0
- data/lib/smartfox_jruby.rb +14 -0
- metadata +130 -0
data/Doubleshot
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'pathname'
|
3
|
+
require Pathname.new(File.dirname(File.expand_path(__FILE__))).join('lib/smartfox_jruby.rb')
|
4
|
+
Doubleshot.new do |config|
|
5
|
+
|
6
|
+
config.project = "smartfox_jruby"
|
7
|
+
config.version = SmartfoxJruby::VERSION
|
8
|
+
|
9
|
+
config.gem 'activesupport', '~> 3.2.8'
|
10
|
+
config.gem 'json'
|
11
|
+
config.gem 'rspec', '~> 2.10.0'
|
12
|
+
|
13
|
+
config.mvn_repository 'http://repo1.maven.org/maven2'
|
14
|
+
config.mvn_repository 'http://artifactory-innovations.i-free.ru/artifactory/ext-release-local'
|
15
|
+
|
16
|
+
config.jar 'com.smartfox2x.client:sfs2x-client-core:1.0.4'
|
17
|
+
config.jar 'com.smartfox2x.client:sfs2x-api-java:1.0.4'
|
18
|
+
config.jar 'org.slf4j:slf4j-log4j12:1.5.10'
|
19
|
+
|
20
|
+
config.gemspec do |spec|
|
21
|
+
spec.summary = "Smartfox client for jruby"
|
22
|
+
spec.description = <<-DESCRIPTION
|
23
|
+
Allows to connect and easily process messages from and to SmartFox game server
|
24
|
+
DESCRIPTION
|
25
|
+
|
26
|
+
spec.homepage = "https://github.com/smecsia/smartfox_jruby"
|
27
|
+
spec.author = "Ilya Sadykov"
|
28
|
+
spec.email = "smecsia@gmail.com"
|
29
|
+
spec.license = "Apache 2.0"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'active_support/all'
|
3
|
+
java_import com.smartfoxserver.v2.entities.data.ISFSObject
|
4
|
+
java_import com.smartfoxserver.v2.entities.data.SFSObject
|
5
|
+
java_import com.smartfoxserver.v2.entities.data.SFSArray
|
6
|
+
java_import java.util.Arrays
|
7
|
+
java_import 'sfs2x.client.core.IEventListener'
|
8
|
+
java_import 'sfs2x.client.core.SFSEvent'
|
9
|
+
java_import 'sfs2x.client.SmartFox'
|
10
|
+
java_import 'sfs2x.client.requests.LoginRequest'
|
11
|
+
java_import 'sfs2x.client.requests.ExtensionRequest'
|
12
|
+
|
13
|
+
module ISFSObject
|
14
|
+
def to_hash
|
15
|
+
HashWithIndifferentAccess.new(JSON.parse(to_json))
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
to_json
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module SmartfoxJruby::SFSUtil
|
24
|
+
class << self
|
25
|
+
def boxing(v, type)
|
26
|
+
case type
|
27
|
+
when :long
|
28
|
+
java.lang.Long.valueOf(v)
|
29
|
+
when :float
|
30
|
+
java.lang.Float.valueOf(v)
|
31
|
+
when :double
|
32
|
+
java.lang.Double.valueOf(v)
|
33
|
+
when :int
|
34
|
+
java.lang.Integer.valueOf(v)
|
35
|
+
when :boolean
|
36
|
+
java.lang.Boolean.valueOf(v)
|
37
|
+
else
|
38
|
+
v
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_java_list(value, type)
|
43
|
+
list = java.util.ArrayList.new
|
44
|
+
value.to_java(type).each do |v|
|
45
|
+
list.add(boxing(v, type))
|
46
|
+
end
|
47
|
+
list
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def SFSObject.from_hash(hash, opts = {})
|
53
|
+
res = SFSObject.new
|
54
|
+
opts ||= {}
|
55
|
+
hash.each_key do |key|
|
56
|
+
value = hash[key]
|
57
|
+
key = key.to_s
|
58
|
+
type = opts["#{key}_type".to_sym]
|
59
|
+
if value.is_a?(Hash)
|
60
|
+
res.putSFSObject(key, from_hash(value, opts[key.to_sym]))
|
61
|
+
elsif value.is_a?(Float)
|
62
|
+
if type == :double
|
63
|
+
res.putDouble(key, value.to_java(:double))
|
64
|
+
else
|
65
|
+
res.putFloat(key, value.to_java(:float))
|
66
|
+
end
|
67
|
+
elsif value.is_a?(Integer)
|
68
|
+
if type == :long
|
69
|
+
res.putLong(key, value.to_java(:long))
|
70
|
+
else
|
71
|
+
res.putInt(key, value.to_java(:int))
|
72
|
+
end
|
73
|
+
elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
|
74
|
+
res.putBool(key, value.to_java(:boolean))
|
75
|
+
elsif value.is_a?(String) or value.is_a?(Symbol)
|
76
|
+
res.putUtfString(key, value.to_java(:string))
|
77
|
+
elsif value.is_a?(Array)
|
78
|
+
unless value.empty?
|
79
|
+
if value[0].is_a?(Hash)
|
80
|
+
array = SFSArray.new
|
81
|
+
value.each { |hash|
|
82
|
+
array.addSFSObject(from_hash(hash, opts[key.to_sym]))
|
83
|
+
}
|
84
|
+
res.putSFSArray(key, array)
|
85
|
+
elsif value[0].is_a?(Float)
|
86
|
+
if type == :double
|
87
|
+
res.putDoubleArray(key, SmartfoxJruby::SFSUtil.to_java_list(value, :double))
|
88
|
+
else
|
89
|
+
res.putFloatArray(key, SmartfoxJruby::SFSUtil.to_java_list(value, :float))
|
90
|
+
end
|
91
|
+
elsif value[0].is_a?(Integer)
|
92
|
+
if type == :long
|
93
|
+
res.putLongArray(key, SmartfoxJruby::SFSUtil.to_java_list(value, :long))
|
94
|
+
else
|
95
|
+
res.putIntArray(key, SmartfoxJruby::SFSUtil.to_java_list(value, :int))
|
96
|
+
end
|
97
|
+
elsif value[0].is_a?(TrueClass) or value[0].is_a?(FalseClass)
|
98
|
+
res.putBoolArray(key, SmartfoxJruby::SFSUtil.to_java_list(value, :boolean))
|
99
|
+
elsif value[0].is_a?(String) or value[0].is_a?(Symbol)
|
100
|
+
res.putUtfStringArray(key, SmartfoxJruby::SFSUtil.to_java_list(value, :string))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
res
|
106
|
+
end
|
107
|
+
|
108
|
+
class Hash
|
109
|
+
include ISFSObject
|
110
|
+
|
111
|
+
def to_sfsobject(opts = {})
|
112
|
+
SFSObject.from_hash(self, opts)
|
113
|
+
end
|
114
|
+
|
115
|
+
def method_missing(m, *args)
|
116
|
+
sfs_object = SFSObject.from_hash(self)
|
117
|
+
if sfs_object.respond_to?(m)
|
118
|
+
sfs_object.send(m, *args)
|
119
|
+
else
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module Kernel
|
126
|
+
class WaitTimeoutException < Exception
|
127
|
+
end
|
128
|
+
|
129
|
+
def wait_with_timeout(timeout = 20, opts = {:sleep_time => 0.1})
|
130
|
+
timeout ||= 20
|
131
|
+
raise "Block is required!" unless block_given?
|
132
|
+
time = Time.now.to_i
|
133
|
+
finished = false
|
134
|
+
while (Time.now.to_i < time + timeout) && !finished
|
135
|
+
sleep opts[:sleep_time] || 0.01 # sleep for 10 ms to wait the response
|
136
|
+
finished = yield
|
137
|
+
end
|
138
|
+
raise WaitTimeoutException.new("Timeout while waiting!") unless finished
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
module Process
|
143
|
+
def self.alive?(pid)
|
144
|
+
return false unless pid
|
145
|
+
begin
|
146
|
+
Process.kill(0, pid)
|
147
|
+
true
|
148
|
+
rescue Errno::ESRCH
|
149
|
+
false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/sfs_worker')
|
2
|
+
|
3
|
+
class SmartfoxJruby::SfsAdapter
|
4
|
+
include IEventListener
|
5
|
+
attr_reader :opts
|
6
|
+
attr_reader :smart_fox
|
7
|
+
attr_reader :worker
|
8
|
+
attr_reader :username
|
9
|
+
DEBUG = false
|
10
|
+
|
11
|
+
def initialize(opts = {})
|
12
|
+
@opts = opts
|
13
|
+
debug "initializing sfs adapter..."
|
14
|
+
@smart_fox = SmartFox.new(false)
|
15
|
+
@connected = false
|
16
|
+
@worker = opts[:worker]
|
17
|
+
@login_as = {}
|
18
|
+
@opts[:timeout] ||= 20
|
19
|
+
@opts[:logger] ||= Logger.new(STDOUT)
|
20
|
+
SFSEvent.constants.each do |evt|
|
21
|
+
evt_value = SFSEvent.const_get(evt)
|
22
|
+
debug "Registering event adapter to self for event '#{evt}' --> '#{evt_value}'..."
|
23
|
+
smart_fox.add_event_listener(evt_value, self)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def connect!(opt = {})
|
28
|
+
opts.reverse_merge!(opt)
|
29
|
+
raise "host and port are required to connect SfsAdapter!" if opts[:host].blank? || opts[:port].blank?
|
30
|
+
debug "connecting to smartfox at #{opts[:host]}:#{opts[:port]} ..."
|
31
|
+
smart_fox.connect(opts[:host], opts[:port])
|
32
|
+
end
|
33
|
+
|
34
|
+
def dispatch(event)
|
35
|
+
debug "got event #{event.type}: #{event}"
|
36
|
+
callback = "on_#{event.type.try(:underscore)}_event"
|
37
|
+
if respond_to?(callback, true)
|
38
|
+
send(callback, event)
|
39
|
+
else
|
40
|
+
debug "Unknown event caught #{event.type} (No method '#{callback}' in #{self})"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def disconnect!
|
45
|
+
smart_fox.disconnect
|
46
|
+
end
|
47
|
+
|
48
|
+
def process!(wrk = nil, &block)
|
49
|
+
wait_with_timeout(@opts[:timeout]) { connected? }
|
50
|
+
@worker = wrk || opts[:worker]
|
51
|
+
if block_given? && connected?
|
52
|
+
instance_eval(&block)
|
53
|
+
else
|
54
|
+
raise "Worker is null!" if @worker.blank?
|
55
|
+
raise "Not connected!" unless connected?
|
56
|
+
end
|
57
|
+
worker.perform!
|
58
|
+
end
|
59
|
+
|
60
|
+
def connected?
|
61
|
+
@connected
|
62
|
+
end
|
63
|
+
|
64
|
+
def on_connect(&block)
|
65
|
+
@on_connect ||= []
|
66
|
+
@on_connect << block if block_given?
|
67
|
+
end
|
68
|
+
|
69
|
+
def on_login(&block)
|
70
|
+
@on_login ||= []
|
71
|
+
@on_login << block if block_given?
|
72
|
+
end
|
73
|
+
|
74
|
+
def login_as(username, password, params = {})
|
75
|
+
params = input_params.to_sfsobject if params.is_a?(Hash)
|
76
|
+
@login_as ||= {:username => username, :password => password, :params => params}
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
###############
|
82
|
+
### HELPERS ###
|
83
|
+
|
84
|
+
def request(*args)
|
85
|
+
worker.request(*args)
|
86
|
+
end
|
87
|
+
|
88
|
+
def worker(opts = {})
|
89
|
+
@worker ||= SfsWorker::Worker.new(@smart_fox, opts)
|
90
|
+
@worker
|
91
|
+
end
|
92
|
+
|
93
|
+
def sfs_send(req)
|
94
|
+
smart_fox.send(req)
|
95
|
+
end
|
96
|
+
|
97
|
+
#########################
|
98
|
+
### EVENTS PROCESSORS ###
|
99
|
+
|
100
|
+
def on_extension_response_event(event)
|
101
|
+
debug "extension_response #{event.arguments.get("cmd")}"
|
102
|
+
@worker.response(event.arguments.get("cmd"), event.arguments.get("params").try(:to_hash) || {}) unless @worker.blank?
|
103
|
+
end
|
104
|
+
|
105
|
+
def on_connection_event(event)
|
106
|
+
debug "on_connection #{event}"
|
107
|
+
@on_connect.each { |block|
|
108
|
+
block.call(event) if block.is_a?(Proc)
|
109
|
+
} unless @on_connect.blank?
|
110
|
+
sfs_send(LoginRequest.new(@login_as[:username], @login_as[:password], opts[:zone],
|
111
|
+
(@login_as[:params] || {}).to_sfsobject));
|
112
|
+
end
|
113
|
+
|
114
|
+
def on_login_event(event)
|
115
|
+
args = event.arguments.get("data").to_hash
|
116
|
+
user = event.arguments.get("user")
|
117
|
+
debug("on_login, args=#{args.to_json}")
|
118
|
+
unless user.blank?
|
119
|
+
info "connected as #{user.name}"
|
120
|
+
@username = user.name
|
121
|
+
@connected = true
|
122
|
+
@on_login.each { |block|
|
123
|
+
block.call(@username) if block.is_a?(Proc)
|
124
|
+
} unless @on_login.blank?
|
125
|
+
else
|
126
|
+
raise "login error '#{args[Aimy::Param.ERROR_MESSAGE]}'"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def on_login_error_event(event)
|
131
|
+
error("error while logging-in: #{event.arguments}")
|
132
|
+
end
|
133
|
+
|
134
|
+
def on_connection_lost_event(event)
|
135
|
+
# nothing to do
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_handshake_event(event)
|
139
|
+
# nothing to do
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
#######################
|
144
|
+
### SERVICE HELPERS ###
|
145
|
+
|
146
|
+
def logger
|
147
|
+
opts[:logger]
|
148
|
+
end
|
149
|
+
|
150
|
+
def debug_enabled?
|
151
|
+
opts[:debug] || DEBUG
|
152
|
+
end
|
153
|
+
|
154
|
+
def debug(msg)
|
155
|
+
logger.debug "#{msg}" if debug_enabled?
|
156
|
+
end
|
157
|
+
|
158
|
+
def info(msg)
|
159
|
+
logger.info "#{msg}"
|
160
|
+
end
|
161
|
+
|
162
|
+
def log(msg)
|
163
|
+
logger.info "#{msg}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def error(msg)
|
167
|
+
logger.error "#{msg}"
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
end
|
172
|
+
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/common')
|
2
|
+
|
3
|
+
java_import java.lang.Runtime
|
4
|
+
java_import java.lang.System
|
5
|
+
|
6
|
+
class SmartfoxJruby::SfsRunner
|
7
|
+
|
8
|
+
def initialize(home_dir, opts = {})
|
9
|
+
@home_dir = home_dir
|
10
|
+
@opts = opts
|
11
|
+
@launched = false
|
12
|
+
@fault = false
|
13
|
+
@pid = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def run!
|
17
|
+
Thread.new do
|
18
|
+
begin
|
19
|
+
info "Changing dir to #{work_dir}"
|
20
|
+
Dir.chdir(work_dir) do
|
21
|
+
info "Running cmd #{cmd}"
|
22
|
+
IO.popen(cmd) do |output|
|
23
|
+
@pid = output.pid
|
24
|
+
info "Running child with pid=#{output.pid}..."
|
25
|
+
output.each do |line|
|
26
|
+
debug(line.gsub(/\n/, ""))
|
27
|
+
@launched = true if line =~ /SmartFoxServer 2X \(.+\) READY!/
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@fault = true
|
31
|
+
end
|
32
|
+
rescue Exception => e
|
33
|
+
error "#{e}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def kill!
|
39
|
+
info "Checking running processes: #{pids.join(",")}"
|
40
|
+
pids.each { |pid|
|
41
|
+
pid = pid.try(:strip).try(:to_i)
|
42
|
+
info "Killing the process with pid=#{pid}..."
|
43
|
+
Process.kill("KILL", pid) if Process.alive?(pid)
|
44
|
+
wait_with_timeout(5) { !Process.alive?(pid) } rescue ""
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_and_wait!(opts = {})
|
49
|
+
run!
|
50
|
+
wait_until_launched_or_fault(opts[:timeout])
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def kill_and_wait!(opts = {})
|
55
|
+
kill!
|
56
|
+
wait_until_terminated(opts[:timeout])
|
57
|
+
end
|
58
|
+
|
59
|
+
def running?
|
60
|
+
!pids.blank? && pids.map { |pid| Process.alive?(pid.to_i) }.include?(true)
|
61
|
+
end
|
62
|
+
|
63
|
+
def launched?
|
64
|
+
@launched
|
65
|
+
end
|
66
|
+
|
67
|
+
def fault?
|
68
|
+
@fault
|
69
|
+
end
|
70
|
+
|
71
|
+
def wait_until_terminated(timeout = nil)
|
72
|
+
wait_with_timeout(timeout) { !running? }
|
73
|
+
end
|
74
|
+
|
75
|
+
def wait_until_launched_or_fault(timeout = nil)
|
76
|
+
wait_with_timeout(timeout) { launched? or fault? }
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def pids
|
82
|
+
pids_out = `pgrep -f "#{work_dir}"`
|
83
|
+
(pids_out.try(:split, "\n")) || []
|
84
|
+
end
|
85
|
+
|
86
|
+
def error(msg)
|
87
|
+
@opts[:logger].error(msg) unless @opts[:logger].blank?
|
88
|
+
end
|
89
|
+
|
90
|
+
def debug(msg)
|
91
|
+
@opts[:logger].debug(msg) unless @opts[:logger].blank?
|
92
|
+
end
|
93
|
+
|
94
|
+
def info(msg)
|
95
|
+
@opts[:logger].info(msg) unless @opts[:logger].blank?
|
96
|
+
end
|
97
|
+
|
98
|
+
def work_dir
|
99
|
+
Pathname.new(@home_dir).join("SFS2X").to_s
|
100
|
+
end
|
101
|
+
|
102
|
+
def env
|
103
|
+
%W[].to_java :string
|
104
|
+
end
|
105
|
+
|
106
|
+
def cmd
|
107
|
+
%Q[#{java_bin} -cp #{classpath} #{java_opts} -Dsmartfox.work_dir="#{work_dir}" com.smartfoxserver.v2.Main]
|
108
|
+
end
|
109
|
+
|
110
|
+
def java_bin
|
111
|
+
Pathname.new(System.getProperty("java.home")).join("bin").join("java")
|
112
|
+
end
|
113
|
+
|
114
|
+
def java_opts
|
115
|
+
%Q[-XX:MaxPermSize=512m -Xms128m -Xms1024m]
|
116
|
+
end
|
117
|
+
|
118
|
+
def classpath
|
119
|
+
%Q[aimy:./:lib/*:lib/Jetty/*:extensions/__lib__/*]
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/common')
|
3
|
+
|
4
|
+
module SmartfoxJruby::SfsWorker
|
5
|
+
class Processor
|
6
|
+
attr_accessor :opts
|
7
|
+
attr_accessor :name
|
8
|
+
attr_accessor :chained
|
9
|
+
attr_accessor :current
|
10
|
+
attr_accessor :blocks
|
11
|
+
|
12
|
+
def initialize(name, opts = {}, &block)
|
13
|
+
@blocks = [block]
|
14
|
+
@name = name
|
15
|
+
@current = self
|
16
|
+
@chained = nil
|
17
|
+
@opts = opts
|
18
|
+
end
|
19
|
+
|
20
|
+
def chain(name, &block)
|
21
|
+
link.chained = Processor.new(name, @opts, &block)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def link
|
26
|
+
p = current
|
27
|
+
p = p.chained while (!p.try(:chained).blank?)
|
28
|
+
p
|
29
|
+
end
|
30
|
+
|
31
|
+
def append(&block)
|
32
|
+
unless link.blank?
|
33
|
+
link.blocks << block
|
34
|
+
else
|
35
|
+
current.blocks << block
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def completed?
|
41
|
+
current.blank?
|
42
|
+
end
|
43
|
+
|
44
|
+
def mandatory?
|
45
|
+
@opts[:mandatory].nil? || @opts[:mandatory]
|
46
|
+
end
|
47
|
+
|
48
|
+
def event(name, data = {})
|
49
|
+
if @current.name.to_s == name.to_s
|
50
|
+
@current.blocks.each { |b| b.call(data) }
|
51
|
+
@current = (@current.chained.blank?) ? nil : @current.chained
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
"Proc[#{current.try(:name)}] --> #{current.try(:chained)}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Request
|
63
|
+
attr_reader :name
|
64
|
+
attr_reader :data
|
65
|
+
|
66
|
+
def initialize(name, data = {})
|
67
|
+
@name = name
|
68
|
+
@data = data
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_extension_request
|
72
|
+
ExtensionRequest.new(@name.to_s, @data)
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s
|
76
|
+
"Req[#{@name}]#{data.to_json}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Response < Request
|
81
|
+
def to_s
|
82
|
+
"Resp[#{@name}]#{data.to_json}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class ContextWorker
|
87
|
+
|
88
|
+
def request(name, data = {})
|
89
|
+
@worker.request(name, data, :context => @context)
|
90
|
+
end
|
91
|
+
|
92
|
+
def append_processor(opts = {}, &block)
|
93
|
+
@worker.append_processor(opts.merge(:context => @context), &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
def expect(name, opts={}, &block)
|
97
|
+
@worker.expect(name, opts.merge(:context => @context), &block)
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialize(context, worker)
|
101
|
+
@context = context
|
102
|
+
@worker = worker
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Worker
|
107
|
+
attr_reader :send_queue
|
108
|
+
attr_reader :events_queue
|
109
|
+
attr_reader :processors
|
110
|
+
attr_reader :smart_fox
|
111
|
+
attr_reader :opts
|
112
|
+
|
113
|
+
def initialize(smart_fox, opts = {})
|
114
|
+
@send_queue = []
|
115
|
+
@processors = []
|
116
|
+
@events_queue = []
|
117
|
+
@opts = opts
|
118
|
+
@opts[:timeout] ||= 20
|
119
|
+
@smart_fox = smart_fox
|
120
|
+
@mutex = Mutex.new
|
121
|
+
@send_qm = Mutex.new
|
122
|
+
end
|
123
|
+
|
124
|
+
def append_processor(opts = {}, &block)
|
125
|
+
debug "appending processor with context #{opts[:context]}"
|
126
|
+
if opts[:context]
|
127
|
+
context = opts[:context].append(&block)
|
128
|
+
else
|
129
|
+
context = processors.last.append(&block)
|
130
|
+
end
|
131
|
+
ContextWorker.new(context, self)
|
132
|
+
end
|
133
|
+
|
134
|
+
def request(name, data = {}, opts = {})
|
135
|
+
debug "create request #{name} with context #{opts[:context]}"
|
136
|
+
data = data.to_sfsobject(opts[:serialize_opts]) if data.is_a?(Hash)
|
137
|
+
req = Request.new(name, data)
|
138
|
+
if !opts[:context].blank? && opts[:context].is_a?(Processor)
|
139
|
+
debug "appending #{req} to processor #{opts[:context]}"
|
140
|
+
context = opts[:context].append {
|
141
|
+
@send_qm.synchronize { send_queue << req }
|
142
|
+
}
|
143
|
+
else
|
144
|
+
debug "adding #{req} to send_queue \n #{dump_state}"
|
145
|
+
context = req
|
146
|
+
@send_qm.synchronize { send_queue << req }
|
147
|
+
end
|
148
|
+
ContextWorker.new(context, self)
|
149
|
+
end
|
150
|
+
|
151
|
+
def expect(name, opts = {}, &block)
|
152
|
+
debug "Expecting to get response #{name} with context #{opts[:context]}"
|
153
|
+
unless opts[:context].blank?
|
154
|
+
if opts[:context].is_a?(Processor)
|
155
|
+
context = opts[:context].chain(name, &block)
|
156
|
+
elsif opts[:context].is_a?(Request)
|
157
|
+
context = Processor.new(name, :context => opts[:context], &block)
|
158
|
+
@mutex.synchronize { processors << context }
|
159
|
+
end
|
160
|
+
else
|
161
|
+
context = Processor.new(name, &block)
|
162
|
+
@mutex.synchronize { processors << context }
|
163
|
+
end
|
164
|
+
ContextWorker.new(context, self)
|
165
|
+
end
|
166
|
+
|
167
|
+
def response(name, data = {})
|
168
|
+
info "Got response #{name} (#{data.to_json})..."
|
169
|
+
@mutex.synchronize { events_queue << Response.new(name, data) }
|
170
|
+
end
|
171
|
+
|
172
|
+
def perform!
|
173
|
+
while !all_events_caught?
|
174
|
+
while !send_queue.blank?
|
175
|
+
req = nil
|
176
|
+
@send_qm.synchronize { req = send_queue.shift }
|
177
|
+
debug "sending request #{req.name}..."
|
178
|
+
smart_fox.send(req.to_extension_request) unless smart_fox.blank?
|
179
|
+
@last_act = Time.now.to_i
|
180
|
+
end
|
181
|
+
process_events
|
182
|
+
check_timeouts
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def all_events_caught?
|
187
|
+
processors.blank? || processors.collect { |p| p.mandatory? }.blank?
|
188
|
+
end
|
189
|
+
|
190
|
+
def wait_all_events_caught
|
191
|
+
debug "Waiting all events being caught..."
|
192
|
+
begin
|
193
|
+
wait_with_timeout { all_events_caught? }
|
194
|
+
rescue WaitTimeoutException => e
|
195
|
+
raise "Failed to catch all the events:"+ dump_state
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def dump_state
|
202
|
+
"events_queue = \n\t-#{events_queue.join("\n\t-")}" +
|
203
|
+
" \n processors = \n\t-#{processors.join("\n\t-")}" +
|
204
|
+
" \n send_queue = \n\t-#{send_queue.join("\n\t-")}"
|
205
|
+
end
|
206
|
+
|
207
|
+
def check_timeouts
|
208
|
+
if !@last_act.blank? && Time.now.to_i > @last_act + opts[:timeout]
|
209
|
+
raise "Worker timeout! Latest interaction was #{Time.now.to_i - @last_act} sec ago!\n #{dump_state}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def process_events
|
214
|
+
wait_with_timeout { !events_queue.blank? } rescue nil
|
215
|
+
ei = 0
|
216
|
+
debug "Processing events..."
|
217
|
+
while ei < events_queue.size
|
218
|
+
event = events_queue[ei]
|
219
|
+
pi = 0
|
220
|
+
debug "Processing event #{event.name}..."
|
221
|
+
while pi < processors.size
|
222
|
+
processor = processors[pi]
|
223
|
+
debug "Trying processor #{processor.name}..."
|
224
|
+
if processor.event(event.name, event.data)
|
225
|
+
@last_act = Time.now.to_i
|
226
|
+
debug "Processor found #{processor}"
|
227
|
+
@mutex.synchronize { events_queue.delete_at(ei) }
|
228
|
+
ei -= 1
|
229
|
+
if processor.completed?
|
230
|
+
debug "Processor completed #{processor.name}."
|
231
|
+
@mutex.synchronize { processors.delete_at(pi) }
|
232
|
+
pi -= 1
|
233
|
+
end
|
234
|
+
end
|
235
|
+
pi += 1
|
236
|
+
end
|
237
|
+
ei += 1
|
238
|
+
end
|
239
|
+
debug "Events processing finished. #{dump_state}"
|
240
|
+
end
|
241
|
+
|
242
|
+
def debug(msg)
|
243
|
+
puts "[#{time}] DEBUG #{msg}" if opts[:debug]
|
244
|
+
end
|
245
|
+
|
246
|
+
def info(msg)
|
247
|
+
puts "[#{time}] INFO #{msg}"
|
248
|
+
end
|
249
|
+
|
250
|
+
def time
|
251
|
+
Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'pathname'
|
4
|
+
Dir[Pathname.new(File.dirname(File.expand_path(__FILE__))).join("#{File.basename(__FILE__)}").join("*.rb")].each { |f| require f }
|
5
|
+
|
6
|
+
####################################################
|
7
|
+
# Main module
|
8
|
+
module SmartfoxJruby
|
9
|
+
# configurable constants
|
10
|
+
VERSION = "0.2"
|
11
|
+
|
12
|
+
|
13
|
+
end
|
14
|
+
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smartfox_jruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: '0.2'
|
6
|
+
platform: java
|
7
|
+
authors:
|
8
|
+
- Ilya Sadykov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 3.2.8
|
21
|
+
none: false
|
22
|
+
requirement: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.8
|
27
|
+
none: false
|
28
|
+
prerelease: false
|
29
|
+
type: :runtime
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: !binary |-
|
37
|
+
MA==
|
38
|
+
none: false
|
39
|
+
requirement: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: !binary |-
|
44
|
+
MA==
|
45
|
+
none: false
|
46
|
+
prerelease: false
|
47
|
+
type: :runtime
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.10.0
|
55
|
+
none: false
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.10.0
|
61
|
+
none: false
|
62
|
+
prerelease: false
|
63
|
+
type: :runtime
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
name: doubleshot
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: !binary |-
|
71
|
+
MA==
|
72
|
+
none: false
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: !binary |-
|
78
|
+
MA==
|
79
|
+
none: false
|
80
|
+
prerelease: false
|
81
|
+
type: :development
|
82
|
+
description: |2
|
83
|
+
Allows to connect and easily process messages from and to SmartFox game server
|
84
|
+
email: smecsia@gmail.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- Doubleshot
|
90
|
+
- lib/smartfox_jruby/common.rb
|
91
|
+
- lib/smartfox_jruby/sfs_adapter.rb
|
92
|
+
- lib/smartfox_jruby/sfs_runner.rb
|
93
|
+
- lib/smartfox_jruby/sfs_worker.rb
|
94
|
+
- lib/smartfox_jruby.rb
|
95
|
+
homepage: https://github.com/smecsia/smartfox_jruby
|
96
|
+
licenses:
|
97
|
+
- Apache 2.0
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options:
|
100
|
+
- "--line-numbers"
|
101
|
+
- "--main"
|
102
|
+
- README.textile
|
103
|
+
- "--title"
|
104
|
+
- smartfox_jruby Documentation
|
105
|
+
- lib
|
106
|
+
- README.textile
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
- target
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: !binary |-
|
115
|
+
MA==
|
116
|
+
none: false
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: !binary |-
|
122
|
+
MA==
|
123
|
+
none: false
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.8.24
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: Smartfox client for jruby
|
130
|
+
test_files: []
|