Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: AntiSol/iarmud
base: 0c982e9467
...
head fork: AntiSol/iarmud
compare: 33fd4558ec
  • 2 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
Commits on May 14, 2011
Dale Maggee CLI client working with threads / mutex ca50285
Dale Maggee cleanup/commenting/dox; $game.queue_event always means *next tick*, e…
…ven inside Event.execute (execute your new event immediately if you want it to happen this tick);
33fd455
Showing with 92 additions and 33 deletions.
  1. +32 −8 README
  2. +41 −23 client.rb
  3. +18 −2 core.rb
  4. +1 −0  events.rb
View
40 README
@@ -50,7 +50,7 @@ but a map object is also available, so another client could for example render t
---SECURITY IMPLICATIONS OF DBUS---
-The server (and, it would appear, DBus's session bus) is not designed to be secure - I can't see a way to tell which application / user is calling a dbus method or listening for signals. It's implied that anyone sitting at the server machine is a server admin.
+The server (and, it would appear, DBus's session bus) is not designed to be secure - I can't see a way to tell which application / user is calling a dbus method or listening for signals. It's implied that anyone sitting at the server machine is a server admin. In terms of server design, we're going for 'not horribly insecure', as opposed to 'NSA-proof' (the nature of the game means that a tinfoil-hat level of security would be very difficult/impossible, e.g: events will always be attackable via cribs due to (semi)deterministic text)
The client should handle most security stuff (i.e: the pidgin MUD will need to map chat sessions to player keys, and return events to the appropriate user)
@@ -62,7 +62,7 @@ having said that, a few things are in place:
- due to the stateless nature of the dbus connection, we'll need a 'sleep' command to tell the server that we're logged out. In-game, the player's character may appear to be 'cuccooned in an inviolable shield during hibernation' or something of the sort. I guess you can't 'untunnel' a room while a player is in hibernation there.
-- the server just starts, then has no user input. this implies that we need a way to do remote administration. to this end, the server generates a 'god key' and spits it to the server log. A function to retrieve the god key should be available, but this function should probably only spit the key out to the log. certain server admin functions will require the god key (and maybe a player key too)
+- the server just starts, then has no user input. this implies that we need a way to do remote administration. to this end, the server generates a 'god key' and spits it to the server log. A way to retrieve the god key should be available, but this function should probably only spit the key out to the log. certain server admin functions will require the god key - A player can type 'god <key>' to make god commands available.
@@ -77,7 +77,7 @@ List of functions:
spawn(playername) - create a new player.
this returns the player key, used for subsequent calls
- playerCommand(playerkey,command) - call for player to perform an action
+ playerCommand(playerkey,command) - call for player to perform an action. returns 'OK' to indicate success, or a message indicating failure
resumeGame(playername,key) - log in / resume a game (existing character)
@@ -86,16 +86,38 @@ List of functions:
List of signals:
- playerEvent(player, encryptedtext) - indicates an event occuring within the world, i.e: 'you go north' or 'grue enters'. the event text is encrypted with
- the player's token, so that only that player can decode it (we don't want players to be able to spy on each other!).
- NOTE: Any one event in the game may fire many playerevents - a player issuing a 'go north' command will result in
- a 'you go north' event for the player who issued the command, a '<player> goes north' event for each player in
- the same location, and a '<player> enters' event for each player in the new location.
+ playerEvent(player, encryptedtext) - indicates an event occuring within the world, i.e: 'you go north' or 'grue enters'.
+
+ The event text is encrypted with the player's token, so that only that player can decode it
+ (we don't want players to be able to spy on each other!).
+
+ Any one event in the game may fire many playerevents - a player issuing a 'go north' command will
+ result in a 'you go north' event for the player who issued the command, a '<player> goes north' event
+ for each player in the same location, and a '<player> enters' event for each player in the new location.
*mapchange(newmap) - the map has changed (not player location, only room changes)
* "non-core" functions / signals: these are not required for core, text-only functionality (and are probably not yet implemented! :P)
+--- GAME TIME / EVENTS ---
+
+The Game processes its event queue once each tick. A tick is an iteration of the main loop. The frequency of ticks is defined by $TICK_GRANULARITY (set at 0.2 - 5 ticks per second). However it should be noted that timing in the game is not precise, for example a tick might include a huge number of events and take more time to elapse. Therefore the tick granularity setting represents the *minimum amount of time between each tick*. For events which need to occur in terms of real-world time (i.e: if you want something to take 10 seconds), there is the 'second_tick' method, which is called by the engine at intervals of one second (roughly; within the timing variance)
+
+Internally, everything that happens in the world is an event. events are added to the event queue and are processed in the order they are added, and are removed from the queue after they are processed - if you want to repeat an event, you'll need to queue it again when it is processed.
+
+At the most basic level, events represent a string ('something happened').
+
+In most cases, a descendant of the PlayerEvent will be what you want - this is an event which happens to a player.
+
+NotificationEvent ane PlayerNotificationEvent are important - these are what raise the DBus 'PlayerEvent' signal.
+
+most events will define an 'execute' method, which is called by the game engine when the event is processed. This is where you make stuff happen in the world.
+
+when executing an event, you might want to raise other events
+
+When a player issues a command, the return value will either be 'OK', or a message indicating why the command was unsuccessful. If the return value is 'OK', then the command was successful, and the client can expect to receive a playerEvent shortly: 'go north' returns 'OK', then a playerEvent with the text 'you go north' will occur.
+
+
--- Proposed Object Model ---
@@ -122,4 +144,6 @@ Mappable - A thing that goes into a Location.
Event - Things that happen in the world
|--FailureEvent - Things that indicate that something didn't happen
|--PlayerEvent - Things that happen to somebody
+ | |---PlayerNotificationEvent - things that raise the DBus PlayerEvent signal
+ |--NotificationEvent - Notifies players at speficied coordinates
View
64 client.rb
@@ -1,6 +1,7 @@
require 'dbus'
require 'blowfish.rb'
require 'base64'
+require 'thread'
class MudClient
attr_reader :bus
@@ -22,21 +23,24 @@ def initialize
if player == @name
crypt = Crypt::Blowfish.new(@token.force_encoding('ASCII'))
text = crypt.decrypt_string(Base64.decode64(encryptedText))
- puts text
+ message text
end
end
-
- puts "Your name?"
- @name = gets.chomp
- @token = @interface.spawn(@name)[0]
-
- puts @interface.playerCommand(@token,'look')
- #rescue
- # message "ERROR: could not connect to DBus!\n"
- # Process.exit
+ rescue
+ message "ERROR: could not connect to DBus!\n"
+ Process.exit
end
message "DBus initialised."
+ puts "Your name?"
+ @name = gets.chomp
+ @token = @interface.spawn(@name)[0]
+
+ message "Your player token is: #{@token}"
+
+ message "Type 'exit' to exit the CLI client.\n"
+
+ message @interface.playerCommand(@token,'look')[0]
end
@@ -45,20 +49,34 @@ def message(text)
end
def run
-
-# t = Thread.new do
- loop = DBus::Main.new
- loop << @bus
- loop.run
-# end
+ mutex = Mutex.new
+ t = Thread.new do
+ #this replaces the Dbus loop...
+ while @running do
+ mutex.synchronize do
+ ready, mert, bork = IO.select([@bus.socket],nil,nil,0.1)
+ if ready
+ @bus.update_buffer
+ while m = @bus.pop_message
+ @bus.process(m)
+ end
+ end
+ end
+ sleep 0.2
+ end
+ end
-# while @running do
-# cmd = gets.chomp
-# puts "> #{cmd}"
-# @running = false if cmd == "exit"
- # puts @interface.playerCommand(@token,cmd)
-# puts "Ready."
-# end
+ while @running do
+
+ cmd = gets.chomp
+ puts "> #{cmd}"
+ @running = false if cmd == "exit"
+
+ mutex.synchronize do
+ puts @interface.playerCommand(@token,cmd)
+ end
+ puts "Ready."
+ end
puts "\n\nExiting...\n"
View
20 core.rb
@@ -113,6 +113,12 @@ def initialize
load('iarmud.map.gz') if File.exists? 'iarmud.map.gz'
@event_queue = []
+
+ #are we processing the queue ATM?
+ @in_queue = false
+
+ #queue for next tick...
+ @next_queue = []
@interpreter = Interpreter.new
@@ -132,8 +138,14 @@ def initialize
#add an event to the event queue.
# the event queue will be processed next tick.
+ # if this is called from within the event queue (i.e from an event's
+ # 'execute' method), the event will occur *next tick*, not at the end
+ # of the current one
def queue_event(event)
event = Event.new(event) if !event.is_a?(Event)
+ if @in_queue #add it to the next tick...
+ return @next_queue.push(event)
+ end
@event_queue.push(event)
end
@@ -189,6 +201,7 @@ def tick
#TODO: put AI / "world-event" code here
#process the event queue...
+ @in_queue = true
@event_queue.each do |evt|
if evt.respond_to?(:execute)
evt.execute
@@ -196,11 +209,14 @@ def tick
#remove event from queue...
@event_queue.shift
end
-
+ @in_queue = false
+ @event_queue = @next_queue
+ @next_queue = []
end
#this tick runs roughly once per second, assuming tick granularity <= 1
- # lowering tick granularity should improve precision
+ # lowering tick granularity (and keeping it a factor of 1)
+ # should improve precision
def second_tick
end
View
1  events.rb
@@ -35,6 +35,7 @@ def execute
end
end
+#notify a specific player
class PlayerNotificationEvent < PlayerEvent
def execute
@player.notify(text)

No commit comments for this range

Something went wrong with that request. Please try again.