carbonmu 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/.gitignore +3 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +161 -0
- data/Guardfile +50 -0
- data/LICENSE.txt +21 -0
- data/README.md +66 -0
- data/Rakefile +10 -0
- data/carbonmu.gemspec +64 -0
- data/config/i18n-tasks.yml +89 -0
- data/config/locales/en.yml +6 -0
- data/config/locales/nl.yml +6 -0
- data/console +9 -0
- data/doc/architecture.png +0 -0
- data/lib/carbonmu.rb +42 -0
- data/lib/commands/locale_command.rb +12 -0
- data/lib/commands/ping_command.rb +9 -0
- data/lib/commands/reboot_command.rb +9 -0
- data/lib/commands/say_command.rb +10 -0
- data/lib/commands/unknown_command.rb +7 -0
- data/lib/core/command.rb +26 -0
- data/lib/core/command_context.rb +12 -0
- data/lib/core/configuration.rb +22 -0
- data/lib/core/connection.rb +19 -0
- data/lib/core/internationalization.rb +16 -0
- data/lib/core/parser.rb +29 -0
- data/lib/core/server.rb +118 -0
- data/lib/core/server_supervision_group.rb +5 -0
- data/lib/core_ext/match_data.rb +7 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/edge_router/edge_connection.rb +39 -0
- data/lib/edge_router/edge_router.rb +104 -0
- data/lib/edge_router/edge_router_supervision_group.rb +5 -0
- data/lib/edge_router/telnet_connection.rb +35 -0
- data/lib/edge_router/telnet_receptor.rb +30 -0
- data/lib/errors.rb +4 -0
- data/lib/game_objects/container.rb +10 -0
- data/lib/game_objects/exit.rb +8 -0
- data/lib/game_objects/game_object.rb +13 -0
- data/lib/game_objects/movable.rb +12 -0
- data/lib/game_objects/player.rb +17 -0
- data/lib/game_objects/room.rb +16 -0
- data/lib/game_objects/thing.rb +7 -0
- data/lib/interactions/notify.rb +11 -0
- data/lib/interactions/reboot.rb +8 -0
- data/lib/ipc/carbon_ipc_socket.rb +21 -0
- data/lib/ipc/ipc_message.rb +54 -0
- data/lib/ipc/read_socket.rb +18 -0
- data/lib/ipc/write_socket.rb +12 -0
- data/lib/version.rb +7 -0
- data/mongoid.yml +12 -0
- data/spec/carbonmu_spec.rb +14 -0
- data/spec/i18n_spec.rb +18 -0
- data/spec/lib/commands/say_command_spec.rb +11 -0
- data/spec/lib/core/command_context_spec.rb +16 -0
- data/spec/lib/core/command_spec.rb +37 -0
- data/spec/lib/core/configuration_spec.rb +37 -0
- data/spec/lib/core/connection_spec.rb +46 -0
- data/spec/lib/core/internationalization_spec.rb +24 -0
- data/spec/lib/core/parser_spec.rb +30 -0
- data/spec/lib/core/server_spec.rb +35 -0
- data/spec/lib/edge_router/edge_connection_spec.rb +9 -0
- data/spec/lib/game_objects/container_spec.rb +9 -0
- data/spec/lib/game_objects/exit_spec.rb +7 -0
- data/spec/lib/game_objects/game_object_spec.rb +12 -0
- data/spec/lib/game_objects/movable_spec.rb +9 -0
- data/spec/lib/game_objects/player_spec.rb +24 -0
- data/spec/lib/game_objects/room_spec.rb +20 -0
- data/spec/lib/game_objects/thing_spec.rb +7 -0
- data/spec/lib/interactions/notify_spec.rb +26 -0
- data/spec/lib/interactions/reboot_spec.rb +9 -0
- data/spec/lib/ipc/carbon_ipc_socket_spec.rb +12 -0
- data/spec/lib/ipc/ipc_message_spec.rb +39 -0
- data/spec/lib/ipc/read_socket_spec.bak +51 -0
- data/spec/lib/ipc/write_socket_spec.bak +25 -0
- data/spec/spec_helper.rb +118 -0
- data/spec/support/helpers.rb +15 -0
- data/spec/support/matchers/be_boolean.rb +5 -0
- data/spec/support/shared_examples/carbon_ipc_socket.rb +3 -0
- data/spec/support/shared_examples/container.rb +3 -0
- data/spec/support/shared_examples/game_object.rb +6 -0
- data/spec/support/shared_examples/movable.rb +4 -0
- data/start +5 -0
- data/start-server-only +8 -0
- metadata +416 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module CarbonMU
|
4
|
+
class TelnetConnection < EdgeConnection
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :@socket, :close, :write
|
7
|
+
|
8
|
+
attr_reader :socket
|
9
|
+
|
10
|
+
def after_initialize(socket)
|
11
|
+
@socket = socket
|
12
|
+
async.run
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
info "*** Received telnet connection #{id} from #{socket.addr[2]}"
|
17
|
+
write "Connected. Your ID is #{id}\n"
|
18
|
+
loop do
|
19
|
+
async.handle_input(read)
|
20
|
+
end
|
21
|
+
rescue EOFError, Errno::ECONNRESET
|
22
|
+
info "*** Telnet connection #{id} disconnected"
|
23
|
+
close
|
24
|
+
terminate
|
25
|
+
end
|
26
|
+
|
27
|
+
def before_shutdown
|
28
|
+
@socket.close unless @socket.closed?
|
29
|
+
end
|
30
|
+
|
31
|
+
def read
|
32
|
+
@socket.readpartial(4096)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'celluloid/autostart'
|
2
|
+
require 'celluloid/io'
|
3
|
+
|
4
|
+
module CarbonMU
|
5
|
+
class TelnetReceptor
|
6
|
+
include Celluloid::IO
|
7
|
+
include Celluloid::Logger
|
8
|
+
|
9
|
+
finalizer :shutdown
|
10
|
+
|
11
|
+
def initialize(host, port)
|
12
|
+
info "*** Starting Telnet receptor on #{host} #{port}."
|
13
|
+
@server = Celluloid::IO::TCPServer.new(host, port)
|
14
|
+
async.run
|
15
|
+
end
|
16
|
+
|
17
|
+
def shutdown
|
18
|
+
@server.close if @server
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
loop { async.handle_connection @server.accept }
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle_connection(socket)
|
26
|
+
tc = TelnetConnection.new(socket)
|
27
|
+
Actor[:edge_router].add_connection(tc)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/errors.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module CarbonMU
|
2
|
+
class GameObject
|
3
|
+
include Mongoid::Document
|
4
|
+
|
5
|
+
field :name, type: String
|
6
|
+
field :description, type: String, default: "You see nothing special."
|
7
|
+
field :_special, type: Symbol
|
8
|
+
|
9
|
+
index(_special: 1)
|
10
|
+
|
11
|
+
validates_uniqueness_of :_special, allow_blank: true
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module CarbonMU
|
2
|
+
module Movable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
include Mongoid::Document
|
6
|
+
|
7
|
+
belongs_to :location, class_name: "CarbonMU::GameObject", inverse_of: :contents, index: true
|
8
|
+
|
9
|
+
validates_presence_of :location
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CarbonMU
|
2
|
+
class Player < GameObject
|
3
|
+
include Mongoid::Document
|
4
|
+
include Movable
|
5
|
+
include Container
|
6
|
+
|
7
|
+
before_validation :default_location
|
8
|
+
|
9
|
+
def default_location
|
10
|
+
self.location ||= Room.starting
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.superadmin
|
14
|
+
find_by(_special: :superadmin_player)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CarbonMU
|
2
|
+
class Room < GameObject
|
3
|
+
include Mongoid::Document
|
4
|
+
include Container
|
5
|
+
|
6
|
+
has_many :incoming_exits, class_name: "CarbonMU::Exit", foreign_key: :destination
|
7
|
+
|
8
|
+
def self.starting
|
9
|
+
find_by(_special: :starting_room)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.lostandfound
|
13
|
+
find_by(_special: :lostandfound_room)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'celluloid/zmq'
|
2
|
+
|
3
|
+
module CarbonMU
|
4
|
+
class CarbonIPCSocket
|
5
|
+
include Celluloid::ZMQ
|
6
|
+
|
7
|
+
attr_accessor :zmq_socket
|
8
|
+
|
9
|
+
def read
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def send(message)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def close
|
18
|
+
@zmq_socket.close if @zmq_socket
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module CarbonMU
|
4
|
+
class IPCMessage
|
5
|
+
def self.valid_ops
|
6
|
+
[:started, :command, :connect, :disconnect, :write, :reboot, :retrieve_existing_connections]
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.required_parameters(op)
|
10
|
+
{
|
11
|
+
write: [:connection_id, :output],
|
12
|
+
connect: [:connection_id],
|
13
|
+
disconnect: [:connection_id],
|
14
|
+
command: [:command, :connection_id],
|
15
|
+
}[op]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.unserialize(text)
|
19
|
+
hash_with_symbol_keys = Hash[MultiJson.load(text).map{|(k,v)| [k.to_sym,v]}]
|
20
|
+
self.new(hash_with_symbol_keys[:op].to_sym, hash_with_symbol_keys)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :params
|
24
|
+
|
25
|
+
def initialize(op, options={})
|
26
|
+
raise ArgumentError, "invalid op specified: #{op}" unless IPCMessage.valid_ops.include?(op)
|
27
|
+
required_parameters = IPCMessage.required_parameters(op) || []
|
28
|
+
provided_parameters = options.keys
|
29
|
+
raise ArgumentError, "missing parameters for #{op} op: #{required_parameters - provided_parameters}" unless (required_parameters & provided_parameters).size == required_parameters.size
|
30
|
+
@params = options.merge(op: op)
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(method_sym, *args, &block)
|
34
|
+
@params.has_key?(method_sym) ? @params[method_sym] : super
|
35
|
+
end
|
36
|
+
|
37
|
+
def respond_to?(method_sym, include_private = false)
|
38
|
+
@params.has_key?(method_sym) ? true : super
|
39
|
+
end
|
40
|
+
|
41
|
+
def serialize
|
42
|
+
MultiJson.dump(params)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"<IPCMessage: #{params}>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(o)
|
50
|
+
o.class == self.class && o.params == self.params
|
51
|
+
end
|
52
|
+
alias_method :eql?, :==
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module CarbonMU
|
2
|
+
class ReadSocket < CarbonIPCSocket
|
3
|
+
def initialize(port = '*')
|
4
|
+
@zmq_socket = Celluloid::ZMQ::PullSocket.new
|
5
|
+
@zmq_socket.bind("tcp://127.0.0.1:#{port}")
|
6
|
+
end
|
7
|
+
|
8
|
+
def read
|
9
|
+
@zmq_socket.read
|
10
|
+
end
|
11
|
+
|
12
|
+
def port_number
|
13
|
+
raw_endpoint = @zmq_socket.get(::ZMQ::LAST_ENDPOINT) || nil
|
14
|
+
return nil if raw_endpoint.nil?
|
15
|
+
raw_endpoint.match(/\:(\d+)/)[1].to_i
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module CarbonMU
|
2
|
+
class WriteSocket < CarbonIPCSocket
|
3
|
+
def initialize(port = '15000')
|
4
|
+
@zmq_socket = Celluloid::ZMQ::PushSocket.new
|
5
|
+
@zmq_socket.connect("tcp://127.0.0.1:#{port}")
|
6
|
+
end
|
7
|
+
|
8
|
+
def send(message)
|
9
|
+
@zmq_socket.send(message)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/version.rb
ADDED
data/mongoid.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CarbonMU do
|
4
|
+
it "has color methods available" do
|
5
|
+
expect("string").to respond_to(:white)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "has a translate method" do
|
9
|
+
str = "Hi there"
|
10
|
+
opts = {foo: "bar"}
|
11
|
+
expect(CarbonMU::Internationalization).to receive(:translate).with(str, [opts])
|
12
|
+
CarbonMU.t(str, opts)
|
13
|
+
end
|
14
|
+
end
|
data/spec/i18n_spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'i18n/tasks'
|
3
|
+
|
4
|
+
describe 'I18n', i18n: true do
|
5
|
+
let(:i18n) { I18n::Tasks::BaseTask.new }
|
6
|
+
let(:missing_keys) { i18n.missing_keys }
|
7
|
+
let(:unused_keys) { i18n.unused_keys }
|
8
|
+
|
9
|
+
skip 'does not have missing keys' do
|
10
|
+
expect(missing_keys).to be_empty,
|
11
|
+
"Missing #{missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
|
12
|
+
end
|
13
|
+
|
14
|
+
skip 'does not have unused keys' do
|
15
|
+
expect(unused_keys).to be_empty,
|
16
|
+
"#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SayCommand do
|
4
|
+
context "syntax" do
|
5
|
+
it { expect(command_parse_results("say foo")).to eq(parse_to(SayCommand, {text: "foo"})) }
|
6
|
+
|
7
|
+
it "requires an argument" do
|
8
|
+
expect(command_parse_results("say")).to eq(parse_to(UnknownCommand))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CommandContext do
|
4
|
+
it "should store its attributes" do
|
5
|
+
connection = double(Connection)
|
6
|
+
player = double(Player)
|
7
|
+
allow(connection).to receive(:player) { player }
|
8
|
+
command = "FOO"
|
9
|
+
params = {boo: "bot"}
|
10
|
+
cc = CommandContext.new(enacting_connection: connection, raw_command: command, params: params)
|
11
|
+
expect(cc.enacting_connection).to eq(connection)
|
12
|
+
expect(cc.enactor).to eq(player)
|
13
|
+
expect(cc.raw_command).to eq(command)
|
14
|
+
expect(cc.params).to eq(params)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestCommand < Command
|
4
|
+
syntax "bar"
|
5
|
+
syntax /foo/
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Command do
|
9
|
+
it "keeps track of defined syntaxes" do
|
10
|
+
expect(TestCommand.syntaxes).to eq(["bar", /foo/])
|
11
|
+
end
|
12
|
+
|
13
|
+
it "sends syntaxes to parser" do
|
14
|
+
expect(Parser).to receive(:register_syntax).with("baz", duck_type(:syntax))
|
15
|
+
expect(Parser).to receive(:register_syntax).with("bat", duck_type(:syntax))
|
16
|
+
|
17
|
+
class ParseTestCommand < Command
|
18
|
+
syntax "baz"
|
19
|
+
syntax "bat"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "command contexts" do
|
24
|
+
before(:each) do
|
25
|
+
@context = double("Context").as_null_object
|
26
|
+
end
|
27
|
+
|
28
|
+
it "stores its context" do
|
29
|
+
my_command = Command.new(@context)
|
30
|
+
expect(my_command.context).to eq(@context)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "raises if .execute called directly on Command" do
|
34
|
+
expect { Command.new(@context).execute}.to raise_exception(NotImplementedError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CarbonMU do
|
4
|
+
describe 'global configuration' do
|
5
|
+
it 'maintains app-wide configurations' do
|
6
|
+
mock_config = Struct.new(:test_setting).new
|
7
|
+
mock_setting = Object.new
|
8
|
+
CarbonMU.configuration = mock_config
|
9
|
+
|
10
|
+
CarbonMU.configure do |config|
|
11
|
+
config.test_setting = mock_setting
|
12
|
+
end
|
13
|
+
|
14
|
+
expect(CarbonMU.configuration.test_setting).to be(mock_setting)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Configuration do
|
20
|
+
let(:config) { Configuration.new }
|
21
|
+
|
22
|
+
describe '#logger' do
|
23
|
+
it 'has a logger' do
|
24
|
+
expect(config.logger).to be_a(Logger)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "doesn't allow an invalid logger" do
|
28
|
+
expect { config.logger = Object.new }.to raise_error(ArgumentError)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#log_ipc_traffic' do
|
33
|
+
it 'has a log_ipc_traffic setting' do
|
34
|
+
expect(config.log_ipc_traffic).to be_boolean
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|