pomelo-citrus 0.0.1
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.
- checksums.yaml +7 -0
- data/README.md +20 -0
- data/Rakefile +0 -0
- data/citrus.gemspec +35 -0
- data/lib/citrus.rb +18 -0
- data/lib/citrus/application.rb +237 -0
- data/lib/citrus/citrus.rb +27 -0
- data/lib/citrus/common/remote/backend/msg_remote.rb +57 -0
- data/lib/citrus/common/remote/frontend/channel_remote.rb +73 -0
- data/lib/citrus/common/remote/frontend/session_remote.rb +108 -0
- data/lib/citrus/common/service/backend_session_service.rb +265 -0
- data/lib/citrus/common/service/channel_service.rb +485 -0
- data/lib/citrus/common/service/connection_service.rb +71 -0
- data/lib/citrus/common/service/filter_service.rb +92 -0
- data/lib/citrus/common/service/handler_service.rb +63 -0
- data/lib/citrus/common/service/session_service.rb +446 -0
- data/lib/citrus/components/backend_session.rb +32 -0
- data/lib/citrus/components/channel.rb +33 -0
- data/lib/citrus/components/component.rb +19 -0
- data/lib/citrus/components/connection.rb +48 -0
- data/lib/citrus/components/connector.rb +265 -0
- data/lib/citrus/components/master.rb +40 -0
- data/lib/citrus/components/monitor.rb +48 -0
- data/lib/citrus/components/proxy.rb +195 -0
- data/lib/citrus/components/push_scheduler.rb +74 -0
- data/lib/citrus/components/remote.rb +71 -0
- data/lib/citrus/components/server.rb +61 -0
- data/lib/citrus/components/session.rb +41 -0
- data/lib/citrus/connectors/commands/handshake.rb +22 -0
- data/lib/citrus/connectors/commands/heartbeat.rb +22 -0
- data/lib/citrus/connectors/commands/kick.rb +22 -0
- data/lib/citrus/connectors/common/coder.rb +21 -0
- data/lib/citrus/connectors/common/handler.rb +21 -0
- data/lib/citrus/connectors/ws_connector.rb +110 -0
- data/lib/citrus/connectors/ws_socket.rb +75 -0
- data/lib/citrus/filters/handler/handler_filter.rb +19 -0
- data/lib/citrus/filters/handler/too_busy.rb +16 -0
- data/lib/citrus/filters/rpc/rpc_filter.rb +19 -0
- data/lib/citrus/filters/rpc/too_busy.rb +16 -0
- data/lib/citrus/master/master.rb +60 -0
- data/lib/citrus/master/starter.rb +73 -0
- data/lib/citrus/master/watchdog.rb +83 -0
- data/lib/citrus/modules/console.rb +45 -0
- data/lib/citrus/modules/console_module.rb +35 -0
- data/lib/citrus/modules/master_watcher.rb +88 -0
- data/lib/citrus/modules/monitor_watcher.rb +86 -0
- data/lib/citrus/monitor/monitor.rb +61 -0
- data/lib/citrus/push_schedulers/buffer.rb +16 -0
- data/lib/citrus/push_schedulers/direct.rb +76 -0
- data/lib/citrus/server/server.rb +327 -0
- data/lib/citrus/util/app_util.rb +203 -0
- data/lib/citrus/util/constants.rb +19 -0
- data/lib/citrus/util/countdown_latch.rb +42 -0
- data/lib/citrus/util/events.rb +14 -0
- data/lib/citrus/util/module_util.rb +68 -0
- data/lib/citrus/util/path_util.rb +50 -0
- data/lib/citrus/util/utils.rb +49 -0
- data/lib/citrus/version.rb +7 -0
- metadata +241 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fc960252d9f68592e4bc7a8152333a82d3fa84c2
|
4
|
+
data.tar.gz: 4801ee7597600f2dc7bcab9497503521f0bc99e5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 833b2b49945dd1623c678ccc0bae9428b36acccb4d6ce4ca031ad142fe02dff5934c0bb0efc31a39bfff4752417241fb1b37c911183e6924d673c13f9cf64c9b
|
7
|
+
data.tar.gz: 770c1e3795f3d5b8dcac4ceb6a412366a0216c99df0eab6dcb24983d77bbbdad335c18723430fc45ed9186ab3d48c090ee6c1b0f0197d649ee71fed4a32cfba9
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
## Welcome to pomelo-citrus
|
2
|
+
|
3
|
+
pomelo-citrus is a simple clone of pomelo written in Ruby using EventMachine.
|
4
|
+
|
5
|
+
## Motivation
|
6
|
+
|
7
|
+
Since NodeJS is influenced by Ruby EventMachine and Python's Twisted model, Ruby should also be able to have its own game server framework like pomelo.
|
8
|
+
|
9
|
+
Ruby is a very expressive and eloquent programming language. I was an RoR programmer before and I really like Ruby, When developing this project, I have used many skills like meta-programming and they give me the real pleasures.
|
10
|
+
|
11
|
+
Recently, I would focus on my daily work, so I open source this project and hope to have more people participate in this project.
|
12
|
+
|
13
|
+
## Todo
|
14
|
+
|
15
|
+
This gem is under development now, remaining about 30% of the workload to do including the tests.
|
16
|
+
|
17
|
+
## Links
|
18
|
+
|
19
|
+
* [EventMachine](https://github.com/eventmachine/eventmachine)
|
20
|
+
* [pomelo](https://github.com/NetEase/pomelo)
|
data/Rakefile
ADDED
File without changes
|
data/citrus.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 16 July 2014
|
4
|
+
|
5
|
+
$:.push File.expand_path('../lib', __FILE__)
|
6
|
+
require 'citrus/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'pomelo-citrus'
|
10
|
+
spec.version = Citrus::VERSION
|
11
|
+
spec.platform = Gem::Platform::RUBY
|
12
|
+
spec.authors = ['MinixLi']
|
13
|
+
spec.email = 'MinixLi1986@gmail.com'
|
14
|
+
spec.description = %q{pomelo-citrus is a simple clone of pomelo, it provides a fast, scalable and distributed game server framework for Ruby}
|
15
|
+
spec.summary = %q{pomelo clone written in Ruby using EventMachine}
|
16
|
+
spec.homepage = 'https://github.com/minixli/pomelo-citrus'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.add_dependency('digest-crc', '~> 0')
|
25
|
+
spec.add_dependency('eventmachine', '~> 0')
|
26
|
+
spec.add_dependency('websocket-eventmachine-server', '~> 0')
|
27
|
+
|
28
|
+
spec.add_dependency('pomelo-citrus-admin', '~> 0')
|
29
|
+
spec.add_dependency('pomelo-citrus-loader', '~> 0')
|
30
|
+
spec.add_dependency('pomelo-citrus-logger', '~> 0')
|
31
|
+
spec.add_dependency('pomelo-citrus-protobuf', '~> 0')
|
32
|
+
spec.add_dependency('pomelo-citrus-protocol', '~> 0')
|
33
|
+
spec.add_dependency('pomelo-citrus-rpc', '~> 0')
|
34
|
+
spec.add_dependency('pomelo-citrus-scheduler', '~> 0')
|
35
|
+
end
|
data/lib/citrus.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 16 July 2014
|
4
|
+
|
5
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
6
|
+
|
7
|
+
require 'eventmachine'
|
8
|
+
|
9
|
+
require 'pomelo-citrus-admin'
|
10
|
+
require 'pomelo-citrus-loader'
|
11
|
+
require 'pomelo-citrus-protobuf'
|
12
|
+
require 'pomelo-citrus-protocol'
|
13
|
+
require 'pomelo-citrus-rpc'
|
14
|
+
require 'pomelo-citrus-scheduler'
|
15
|
+
|
16
|
+
require 'citrus/util/constants'
|
17
|
+
require 'citrus/util/utils'
|
18
|
+
require 'citrus/citrus'
|
@@ -0,0 +1,237 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 16 July 2014
|
4
|
+
|
5
|
+
require 'citrus/util/app_util'
|
6
|
+
|
7
|
+
module Citrus
|
8
|
+
# Application
|
9
|
+
#
|
10
|
+
#
|
11
|
+
class Application
|
12
|
+
include Utils::AppUtil
|
13
|
+
include Utils::EventEmitter
|
14
|
+
|
15
|
+
attr_reader :env, :base, :type, :start_id
|
16
|
+
attr_reader :cur_server, :server_id, :server_type
|
17
|
+
attr_reader :master, :servers_map
|
18
|
+
attr_reader :components, :modules_registered
|
19
|
+
|
20
|
+
# Initialize the application
|
21
|
+
#
|
22
|
+
# @param [Hash] args Option
|
23
|
+
def initialize args={}
|
24
|
+
@env = nil
|
25
|
+
@type = nil
|
26
|
+
@start_id = nil
|
27
|
+
|
28
|
+
@base = args[:base] || Dir.pwd
|
29
|
+
@settings = {}
|
30
|
+
|
31
|
+
# current server info
|
32
|
+
@cur_server = nil
|
33
|
+
@server_id = nil
|
34
|
+
@server_type = nil
|
35
|
+
@start_time = nil
|
36
|
+
|
37
|
+
# global server info
|
38
|
+
@master = nil
|
39
|
+
@servers = {}
|
40
|
+
@servers_map = {}
|
41
|
+
@server_types = []
|
42
|
+
@server_type_maps = {}
|
43
|
+
|
44
|
+
# lifecycle callbacks
|
45
|
+
@lifecycle_cbs = {}
|
46
|
+
|
47
|
+
@components = {}
|
48
|
+
@modules_registered = {}
|
49
|
+
|
50
|
+
default_configuration
|
51
|
+
|
52
|
+
@state = :state_inited
|
53
|
+
|
54
|
+
# singleton pattern
|
55
|
+
eval %Q{
|
56
|
+
class ::Citrus::Application
|
57
|
+
private_class_method :new
|
58
|
+
end
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Start the application
|
63
|
+
def start &block
|
64
|
+
@start_time = Time.now.to_f
|
65
|
+
if @state != :state_inited
|
66
|
+
block_given? and yield Exception.new 'application double start'
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
if @start_id
|
71
|
+
if @start_id != 'master'
|
72
|
+
Starter.run_servers self
|
73
|
+
return
|
74
|
+
end
|
75
|
+
else
|
76
|
+
if @type && @type != :all && @type != :master
|
77
|
+
Starter.run_servers self
|
78
|
+
return
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
load_default_components
|
83
|
+
|
84
|
+
if before_cb = @lifecycle_cbs[:before_startup]
|
85
|
+
before_cb.call self, &proc{
|
86
|
+
start_up &block
|
87
|
+
}
|
88
|
+
else
|
89
|
+
start_up &block
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Start up
|
94
|
+
def start_up &block
|
95
|
+
opt_components(@components.values, :start) { |err|
|
96
|
+
@state = :state_started
|
97
|
+
if err
|
98
|
+
block_given? and yield err
|
99
|
+
else
|
100
|
+
after_start &block
|
101
|
+
end
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
# Lifecycle callback for after start
|
106
|
+
def after_start &block
|
107
|
+
if @state != :state_started
|
108
|
+
block_given? and yield RuntimeError.new 'application is not running now'
|
109
|
+
return
|
110
|
+
end
|
111
|
+
|
112
|
+
opt_components(@components.values, :after_start) { |err|
|
113
|
+
if after_cb = @lifecycle_cbs[:after_start]
|
114
|
+
after_cb.call self, &proc{
|
115
|
+
block_given? and yield err
|
116
|
+
}
|
117
|
+
else
|
118
|
+
block_given? and yield err
|
119
|
+
end
|
120
|
+
}
|
121
|
+
puts used_time = Time.now.to_f - @start_time
|
122
|
+
end
|
123
|
+
|
124
|
+
# Stop components
|
125
|
+
#
|
126
|
+
#
|
127
|
+
def stop force=false
|
128
|
+
end
|
129
|
+
|
130
|
+
# Assign `setting` to `value`
|
131
|
+
#
|
132
|
+
# @param [Symbol] setting
|
133
|
+
# @param [Object] value
|
134
|
+
def set setting, value
|
135
|
+
@settings[setting] = value
|
136
|
+
return self
|
137
|
+
end
|
138
|
+
|
139
|
+
# Get property from setting
|
140
|
+
#
|
141
|
+
# @param [Symbol] setting
|
142
|
+
def get setting
|
143
|
+
@settings[setting]
|
144
|
+
end
|
145
|
+
|
146
|
+
# Configure callback for the specified env and server type
|
147
|
+
def configure *args, &block
|
148
|
+
args.length > 0 ? env = args[0].to_s : env = 'all'
|
149
|
+
args.length > 1 ? server_type = args[1].to_s : server_type = 'all'
|
150
|
+
if env == 'all' || contains(@env.to_s, env)
|
151
|
+
if server_type == 'all' || contains(@server_type, server_type)
|
152
|
+
instance_eval &block if block
|
153
|
+
end
|
154
|
+
end
|
155
|
+
return self
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get server infos by server type
|
159
|
+
#
|
160
|
+
# @param [String] type
|
161
|
+
def get_servers_by_type type
|
162
|
+
@server_type_maps[type]
|
163
|
+
end
|
164
|
+
|
165
|
+
# Check whether a server is a frontend
|
166
|
+
#
|
167
|
+
# @param [Object] server
|
168
|
+
def frontend? server=nil
|
169
|
+
server ? server[:frontend] == true : @cur_server[:frontend] == true
|
170
|
+
end
|
171
|
+
|
172
|
+
# Check whether a server is a backend
|
173
|
+
#
|
174
|
+
# @param [Object] server
|
175
|
+
def backend? server=nil
|
176
|
+
not frontend? server
|
177
|
+
end
|
178
|
+
|
179
|
+
# Check whether a server is a master
|
180
|
+
def master?
|
181
|
+
@server_type == 'master'
|
182
|
+
end
|
183
|
+
|
184
|
+
# Add new servers at runtime
|
185
|
+
#
|
186
|
+
# @param [Array] sinfos
|
187
|
+
def add_servers sinfos
|
188
|
+
return unless sinfos && !sinfos.empty?
|
189
|
+
sinfos.each { |sinfo|
|
190
|
+
# update global server map
|
191
|
+
@servers[sinfo[:server_id]] = sinfo
|
192
|
+
|
193
|
+
# update global server type map
|
194
|
+
slist = @server_type_maps[sinfo[:server_type]] ||= []
|
195
|
+
replace_server slist, sinfo
|
196
|
+
|
197
|
+
# update global server type list
|
198
|
+
if !@server_types.member? sinfo[:server_type]
|
199
|
+
@server_types << sinfo[:server_type]
|
200
|
+
end
|
201
|
+
}
|
202
|
+
emit 'add_servers', sinfos
|
203
|
+
end
|
204
|
+
|
205
|
+
# Remove servers at runtime
|
206
|
+
#
|
207
|
+
# @param [Array] server_ids
|
208
|
+
def remove_servers server_ids
|
209
|
+
end
|
210
|
+
|
211
|
+
# Replace servers at runtime
|
212
|
+
#
|
213
|
+
# @param [Array] sinfos
|
214
|
+
def replace_servers sinfos
|
215
|
+
end
|
216
|
+
|
217
|
+
# Remove server at runtime
|
218
|
+
#
|
219
|
+
# @param [String] server_id
|
220
|
+
def remove_server server_id
|
221
|
+
end
|
222
|
+
|
223
|
+
# Replace server at runtime
|
224
|
+
#
|
225
|
+
# @param [Array] slist
|
226
|
+
# @param [Hash] server_info
|
227
|
+
def replace_server slist, sinfo
|
228
|
+
slist.each_with_index { |s, index|
|
229
|
+
if s[:server_id] == sinfo[:server_id]
|
230
|
+
slist[index] = sinfo
|
231
|
+
return
|
232
|
+
end
|
233
|
+
}
|
234
|
+
slist << sinfo
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 16 July 2014
|
4
|
+
|
5
|
+
require 'citrus/application'
|
6
|
+
require 'citrus/util/events'
|
7
|
+
|
8
|
+
module Citrus
|
9
|
+
# Create a citrus application
|
10
|
+
#
|
11
|
+
# @param [Hash] args
|
12
|
+
def self.create_app args={}
|
13
|
+
app = Application.new args
|
14
|
+
|
15
|
+
Object.class_eval {
|
16
|
+
def app
|
17
|
+
return app
|
18
|
+
end
|
19
|
+
}
|
20
|
+
return app
|
21
|
+
end
|
22
|
+
|
23
|
+
# Load all the components
|
24
|
+
Dir.glob(File.expand_path('../../citrus/components/*.rb', __FILE__)).each { |filepath|
|
25
|
+
require filepath
|
26
|
+
}
|
27
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 24 July 2014
|
4
|
+
|
5
|
+
module Citrus
|
6
|
+
# Common
|
7
|
+
#
|
8
|
+
#
|
9
|
+
module Common
|
10
|
+
# Remote
|
11
|
+
#
|
12
|
+
#
|
13
|
+
module Remote
|
14
|
+
# Backend
|
15
|
+
#
|
16
|
+
#
|
17
|
+
module Backend
|
18
|
+
# MsgRemote
|
19
|
+
#
|
20
|
+
#
|
21
|
+
class MsgRemote
|
22
|
+
# Create a new remote message service
|
23
|
+
#
|
24
|
+
# @param [Object] app
|
25
|
+
def initialize app
|
26
|
+
@app = app
|
27
|
+
end
|
28
|
+
|
29
|
+
# Forward message from frontend server
|
30
|
+
#
|
31
|
+
# @param [Hash] msg
|
32
|
+
# @param [Object] session
|
33
|
+
def forwardMessage msg, session, &block
|
34
|
+
server = @app.components['server']
|
35
|
+
session_service = @app.components['backend_session'].service
|
36
|
+
|
37
|
+
unless server
|
38
|
+
block_given? and yield Exception.new 'server component not enabled'
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
unless session_service
|
43
|
+
block_given? and yield Exception.new 'backend session component not enabled'
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
backend_session = session_service.create session
|
48
|
+
|
49
|
+
server.handle(msg, backend_session) { |err, resp, args|
|
50
|
+
block_given? and yield err, resp, args
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 24 July 2014
|
4
|
+
|
5
|
+
module Citrus
|
6
|
+
# Common
|
7
|
+
#
|
8
|
+
#
|
9
|
+
module Common
|
10
|
+
# Remote
|
11
|
+
#
|
12
|
+
#
|
13
|
+
module Remote
|
14
|
+
# Frontend
|
15
|
+
#
|
16
|
+
#
|
17
|
+
module Frontend
|
18
|
+
# ChannelRemote
|
19
|
+
#
|
20
|
+
#
|
21
|
+
class ChannelRemote
|
22
|
+
# Create a new remote channel service
|
23
|
+
#
|
24
|
+
# @param [Object] app
|
25
|
+
def initialize app
|
26
|
+
@app = app
|
27
|
+
end
|
28
|
+
|
29
|
+
# Push message to client by uids
|
30
|
+
#
|
31
|
+
# @param [String] route
|
32
|
+
# @param [Hash] msg
|
33
|
+
# @param [Array] uids
|
34
|
+
# @param [Hash] args
|
35
|
+
def pushMessage route, msg, uids, args, &block
|
36
|
+
if msg.empty?
|
37
|
+
block_given? and yield Exception.new 'can not send empty message'
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
connector = @app.components['connector']
|
42
|
+
|
43
|
+
session_service = @app.session_service
|
44
|
+
sids = []; fails = []
|
45
|
+
uids.each { |uid|
|
46
|
+
sessions = session_service.get_by_uid uid
|
47
|
+
if sessions
|
48
|
+
sessions.each { |session|
|
49
|
+
sids << session.id
|
50
|
+
}
|
51
|
+
else
|
52
|
+
fails << uid
|
53
|
+
end
|
54
|
+
}
|
55
|
+
connector.send(nil, route, msg, sids, args) { |err|
|
56
|
+
block_given? and yield err, fails
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Broadcast to all the clients connected with current frontend server
|
61
|
+
#
|
62
|
+
# @param [String] route
|
63
|
+
# @param [Hash] msg
|
64
|
+
# @param [Hash] args
|
65
|
+
def broadcast route, msg, args, &block
|
66
|
+
connector = @app.components['connector']
|
67
|
+
connector.send nil, route, msg, nil, args, &block
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|