pomelo-citrus 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|