pomelo-citrus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +20 -0
  3. data/Rakefile +0 -0
  4. data/citrus.gemspec +35 -0
  5. data/lib/citrus.rb +18 -0
  6. data/lib/citrus/application.rb +237 -0
  7. data/lib/citrus/citrus.rb +27 -0
  8. data/lib/citrus/common/remote/backend/msg_remote.rb +57 -0
  9. data/lib/citrus/common/remote/frontend/channel_remote.rb +73 -0
  10. data/lib/citrus/common/remote/frontend/session_remote.rb +108 -0
  11. data/lib/citrus/common/service/backend_session_service.rb +265 -0
  12. data/lib/citrus/common/service/channel_service.rb +485 -0
  13. data/lib/citrus/common/service/connection_service.rb +71 -0
  14. data/lib/citrus/common/service/filter_service.rb +92 -0
  15. data/lib/citrus/common/service/handler_service.rb +63 -0
  16. data/lib/citrus/common/service/session_service.rb +446 -0
  17. data/lib/citrus/components/backend_session.rb +32 -0
  18. data/lib/citrus/components/channel.rb +33 -0
  19. data/lib/citrus/components/component.rb +19 -0
  20. data/lib/citrus/components/connection.rb +48 -0
  21. data/lib/citrus/components/connector.rb +265 -0
  22. data/lib/citrus/components/master.rb +40 -0
  23. data/lib/citrus/components/monitor.rb +48 -0
  24. data/lib/citrus/components/proxy.rb +195 -0
  25. data/lib/citrus/components/push_scheduler.rb +74 -0
  26. data/lib/citrus/components/remote.rb +71 -0
  27. data/lib/citrus/components/server.rb +61 -0
  28. data/lib/citrus/components/session.rb +41 -0
  29. data/lib/citrus/connectors/commands/handshake.rb +22 -0
  30. data/lib/citrus/connectors/commands/heartbeat.rb +22 -0
  31. data/lib/citrus/connectors/commands/kick.rb +22 -0
  32. data/lib/citrus/connectors/common/coder.rb +21 -0
  33. data/lib/citrus/connectors/common/handler.rb +21 -0
  34. data/lib/citrus/connectors/ws_connector.rb +110 -0
  35. data/lib/citrus/connectors/ws_socket.rb +75 -0
  36. data/lib/citrus/filters/handler/handler_filter.rb +19 -0
  37. data/lib/citrus/filters/handler/too_busy.rb +16 -0
  38. data/lib/citrus/filters/rpc/rpc_filter.rb +19 -0
  39. data/lib/citrus/filters/rpc/too_busy.rb +16 -0
  40. data/lib/citrus/master/master.rb +60 -0
  41. data/lib/citrus/master/starter.rb +73 -0
  42. data/lib/citrus/master/watchdog.rb +83 -0
  43. data/lib/citrus/modules/console.rb +45 -0
  44. data/lib/citrus/modules/console_module.rb +35 -0
  45. data/lib/citrus/modules/master_watcher.rb +88 -0
  46. data/lib/citrus/modules/monitor_watcher.rb +86 -0
  47. data/lib/citrus/monitor/monitor.rb +61 -0
  48. data/lib/citrus/push_schedulers/buffer.rb +16 -0
  49. data/lib/citrus/push_schedulers/direct.rb +76 -0
  50. data/lib/citrus/server/server.rb +327 -0
  51. data/lib/citrus/util/app_util.rb +203 -0
  52. data/lib/citrus/util/constants.rb +19 -0
  53. data/lib/citrus/util/countdown_latch.rb +42 -0
  54. data/lib/citrus/util/events.rb +14 -0
  55. data/lib/citrus/util/module_util.rb +68 -0
  56. data/lib/citrus/util/path_util.rb +50 -0
  57. data/lib/citrus/util/utils.rb +49 -0
  58. data/lib/citrus/version.rb +7 -0
  59. metadata +241 -0
@@ -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
@@ -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)
File without changes
@@ -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
@@ -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