class Account

The Account class handles connection login and passes them to character.

Attributes

character[RW]
conn[RW]
echo[RW]
mode[RW]
terminal[RW]
termsize[RW]

Public Class Methods

new(conn) click to toggle source

Create an Account connection. This is a temporary object that handles login for character and gets them connected.

conn

The session associated with this Account connection.

return

A handle to the Account object.

# File lib/core/account.rb, line 32
def initialize(conn)
  super("",nil)
  self.passwd = nil
  self.color = false
  self.characters = []
  @conn = conn                # Reference to network session (connection)
  @mode = :initialize
  @echo = false
  @termsize = nil
  @terminal = nil
  @checked = 3                # Login retry counter - on 0 disconnect
  @account = nil              # used only during sign-in process
  @character = nil            # reference to the currently played Character.
end

Public Instance Methods

append_echo(msg) click to toggle source

If echo hasn’t been negotiated, we want to leave the cursor after the message prompt, so we prepend linefeeds in front of messages. This is hackish.

# File lib/core/account.rb, line 327
def append_echo(msg)
  @echo ? msg : "\n" + msg
end
character_menu() click to toggle source
# File lib/core/account.rb, line 379
def character_menu
  str = '[color Yellow]'
  characters.each_index do |i|
    str << "#{i}) #{get_object(characters[i]).name}\n"
  end
  str << "Pick a character>[/color] "
end
disconnect(msg=nil) click to toggle source

Disconnects this account

# File lib/core/account.rb, line 371
def disconnect(msg=nil)
  publish("[home 1,1][scrreset][clear]") if vtsupport?
  publish(msg + "\n") if msg
  publish("Bye!\n")
  publish(:quit)
  unsubscribe_all
end
login_menu() click to toggle source
# File lib/core/account.rb, line 387
def login_menu
  "[color Yellow]1) Create a character\n2) Play\nQ) Quit\n>[/color] "
end
parse_menu(msg) click to toggle source

Handles message while in the login menu - called by parse_messages. This was refactored out of #parse_messages for length reasons.

msg

The message string

@mode tracks the state changes, This routine is entered by any @modes staring with :menu.

The following state transition diagram illustrates the possible transitions.

:menu -> :menucr Create a character

-> :menupl       Play a character

:menucr -> :playing Get character name, create character and play

# File lib/core/account.rb, line 270
def parse_menu(msg)
  case @mode
  when :menu
    case msg
    when %r^1/
      sendmsg(append_echo("Enter character name> "))
      @mode = :menucr
    when %r^2/
      if characters.size == 0
        sendmsg(append_echo(login_menu))
        @mode = :menu
      else
        sendmsg(append_echo(character_menu))
        @mode = :menupl
      end
    when %r^Q/
      disconnect
    else                        # Any other key
      sendmsg(append_echo(login_menu))
      @mode = :menu
    end
  when :menucr
    if msg.proper_name.empty?
      sendmsg(append_echo(login_menu))
      @mode = :menu
    else
      @character = new_char(msg.proper_name)
      @conn.set(:color, color)
      welcome
      @mode = :playing
    end
  when :menupl
    case msg
    when %r(\d+)/
      if $1.to_i >= characters.size
        sendmsg(append_echo(character_menu))
      else
        @character = get_object(characters[$1.to_i])
        # make the character non-swappable so we dont lose references
        Engine.instance.db.makenoswap(@character.id)
        world.connected_characters << @character.id
        @character.account = self
        welcome
        @mode = :playing
      end
    else
      sendmsg(append_echo(login_menu))
      @mode = :menu
    end
  else
    log.error "Account#parse_menu unknown :mode - #{@mode.inspect}"
  end
end
parse_messages(msg) click to toggle source

Handles String messages from Connection - called by update. This was refactored out of #update for length reasons.

msg

The message string

@mode tracks the state changes, The Account is created with the initial state of :initialize. The following state transition diagram illustrates the possible transitions.

:intialize -> :name Set when Account:update receives :initdone msg :name -> :password Sets @login_name and finds @account

:playing      Creates a new character if Guest account

:password -> :newacct Sets @login_passwd

-> :menu         Good passwd, switches account, if account_system
                 option on goes to menu
-> :playing      Good passwd, switches account, loads character
-> :name         Bad passwd
-> disconnect    Bad passwd, exceeds @check attempts
                 (see Account#disconnect)

:newacct -> :menu If account_system option on goes to menu

-> :playing        Creates new character, adds account

:menu -> #parse_menu Redirect message (see #parse_menu) :playing -> @character Redirect message (see Character#parse)

# File lib/core/account.rb, line 148
def parse_messages(msg)
  case @mode
  when :initialize
    # ignore everything until negotiation done
  when :name
    publish("[clearline]") if vtsupport?
    @login_name = msg.proper_name
    if options['guest_accounts'] && @login_name =~ %rGuest/
      self.name = "Guest#{id}"
      @character = new_char
      put_object(self)
      world.all_accounts << id
      # make the account non-swappable so we dont lose connection
      Engine.instance.db.makenoswap(id)
      @conn.set(:color, color)
      welcome
      @mode = :playing
    elsif @login_name.empty?
      sendmsg(append_echo("login> "))
      @mode = :name
    else
      acctid = world.all_accounts.find {|a|
        @login_name == get_object(a).name
      }
      @account = get_object(acctid)
      sendmsg(append_echo("password> "))
      @conn.set(:hide, true)
      @mode = :password
    end
  when :password
    @login_passwd = msg
    @conn.set(:hide, false)
    if @account.nil?  # new account
      sendmsg(append_echo("Create new user?\n'Y/y' to create, Hit enter to retry login> "))
      @mode = :newacct
    else
      if @login_passwd.is_passwd?(@account.passwd)  # good login
        # deregister all observers here and on connection
        unsubscribe_all
        @conn.unsubscribe_all
        # reregister all observers to @account
        @conn.subscribe(@account.id)
        # make the account non-swappable so we dont lose connection
        Engine.instance.db.makenoswap(@account.id)
        @conn.set(:color, @account.color)
        switch_acct(@account)
        # Check if this account already logged in
        reconnect = false
        if @account.subscriber_count > 0
          @account.publish(:reconnecting)
          @account.unsubscribe_all
          reconnect = true
        end
        @account.subscribe(@conn)
        if options['account_system']
          @account.sendmsg(append_echo(login_menu))
          @account.mode = :menu
        else
          @character = get_object(@account.characters.first)
          # make the character non-swappable so we dont lose references
          Engine.instance.db.makenoswap(@character.id)
          world.connected_characters << @character.id
          @character.account = @account
          @account.character = @character
          welcome(reconnect)
          @account.mode = :playing
        end
      else  # bad login
        @checked -= 1
        sendmsg(append_echo("Sorry wrong password."))
        if @checked < 1
          disconnect
        else
          @mode = :name
          sendmsg(append_echo("login> "))
        end
      end
    end
  when :newacct
    if msg =~ %r^y/
      self.name = @login_name
      self.passwd = @login_passwd.encrypt
      put_object(self)
      # make the account non-swappable so we dont lose connection
      Engine.instance.db.makenoswap(id)
      world.all_accounts << id
      @conn.set(:color, color)
      if options['account_system']
        sendmsg(append_echo(login_menu))
        @mode = :menu
      else
        @character = new_char
        welcome
        @mode = :playing
      end
    else
      @mode = :name
      sendmsg(append_echo("login> "))
    end
  when :menu, :menucr, :menupl
    parse_menu(msg)
  when :playing
    @character.parse(msg)
  else
    log.error "Account#parse_messages unknown :mode - #{@mode.inspect}"
  end
end
prompt() click to toggle source
# File lib/core/account.rb, line 338
  def prompt
    if vtsupport?
      publish("[cursave][home #{@termsize[1]-2},1]" +
        "[color Yellow on Red]#{" "*@termsize[0]}[/color]" +
        "[home #{@termsize[1]-1},1][clearline][color Magenta](#{name})[#{@mode}][/color]" +
        "[currest][clearline]> ")
      publish("[home #{@termsize[1]-2},1]" +
        "[color Yellow on Red]#{" "*@termsize[0]}[/color]" +
        "[home #{@termsize[1]-1},1][clearline][color Magenta](#{name})[#{@mode}][/color]" +
        "[home #{@termsize[1]},1][clearline]> ")
    else
#      publish("> ")
    end
  end
sendmsg(msg) click to toggle source
# File lib/core/account.rb, line 331
def sendmsg(msg)
  publish("[cursave][home #{@termsize[1]-3},1]") if vtsupport?
  publish(msg)
  publish("[currest]") if vtsupport?
  prompt
end
status_rept() click to toggle source
# File lib/core/account.rb, line 355
def status_rept
  str = "Terminal: #{@terminal}\n"
  str << "Terminal size: #{@termsize[0]} X #{@termsize[1]}\n"
  str << "Colors toggled #{@color ? '[COLOR Magenta]ON[/COLOR]' : 'OFF' }\n"
  str << "Echo is #{@echo ? 'ON' : 'OFF' }\n"
  str << "ZMP is #{@conn.query(:zmp) ? 'ON' : 'OFF' }\n"
end
toggle_color() click to toggle source
# File lib/core/account.rb, line 363
def toggle_color
  color ? self.color = false : self.color = true
  @conn.set(:color,color)
  "Colors toggled #{color ? '[COLOR Magenta]ON[/COLOR]' : 'OFF' }\n"
end
update(msg) click to toggle source

Receives messages from a Connection being observed and handles login state.

msg

The message string

This supports the following:

:disconnected
  • This symbol from the server informs us that the

Connection has disconnected.

:initdone
  • This symbol from the server indicates that the Connection is done setting up and done negotiating an initial state. It triggers us to start sending output and parsing input.

:termsize
  • This is sent everytime the terminal size changes (NAWS)

String
  • A String is assumed to be input from the Session and we send it to parse_messages.

# File lib/core/account.rb, line 62
def update(msg)
  case msg
  # Handle disconnection from server
  # Note that publishing a :quit event (see #disconnect) will return a
  #  :disconnected event when server has closed the connection.
  # Guest accounts and characters are deleted here.
  when :disconnected
    @conn = nil
    unsubscribe_all
    Engine.instance.db.makeswap(id)
    if @character
      world.connected_characters.delete(@character.id)
      world.connected_characters.each do |pid|
        add_event(@character.id,pid,:show,"#{name} has disconnected.")
      end
      Engine.instance.db.makeswap(@character.id)
      @character.account = nil
      if @character.name =~ %rGuest/
        world.all_characters.delete(@character.id)
        delete_object(@character.id)
      end
      @character = nil
    end
    if name =~ %rGuest/
      world.all_accounts.delete(id)
      delete_object(id)
    end
  # Issued when a NAWS event occurs
  # Currently this clears and resets the screen.  Ideally it should attempt
  # to redraw it.
  when :termsize
    @termsize = @conn.query(:termsize)
    if vtsupport?
      publish("[home #{@termsize[1]},1][clearline][cursave]" +
        "[home 1,1][scrreset][clear][scrreg 1,#{@termsize[1]-3}][currest]")
    end
  # Negotiation with client done.  Start talking to it.
  when :initdone
    @echo = @conn.query(:echo)
    @termsize = @conn.query(:termsize)
    @terminal = @conn.query(:terminal)
    if vtsupport?
      publish("[home #{@termsize[1]},1][clearline][cursave]" +
        "[home 1,1][scrreset][clear][scrreg 1,#{@termsize[1]-3}][currest]")
      sendmsg(LOGO)
    end
    sendmsg(BANNER)
    sendmsg(append_echo("login> "))
    @mode = :name
  # This is a message from our user
  when String
    parse_messages(msg)
  else
    log.error "Account#update unknown message - #{msg.inspect}"
  end
rescue
  # We squash and print out all exceptions here.  There is no reason to
  # throw these back at the Connection.
  log.error $!
end
vtsupport?() click to toggle source
# File lib/core/account.rb, line 391
def vtsupport?
  @terminal =~ %r^vt|xterm/
end