Games-Axmud

 view release on metacpan or  search on metacpan

lib/Games/Axmud/Client.pm  view on Meta::CPAN

    use warnings;
#   use diagnostics;

    use Glib qw(TRUE FALSE);
    # Include module here, as well as in axmud.pl, so that .../t/00-compile.t won't fail
    use Archive::Tar;

    our @ISA = qw(Games::Axmud);

    ##################
    # Constructors

    sub new {

        # Called by axmud.pl on startup
        #
        # Expected arguments
        #   (none besides $class)
        #
        # Return values
        #   'undef' on improper arguments
        #   Blessed reference to the newly-created object on success. The calling function sets the
        #       $CLIENT global variable

        my ($class, $check) = @_;

        # Local variables
        my (
            $urlRegex, $shortRegex, $emailRegex,
            @cmdList,
            %soundHash, %msspHash,
        );

        # Check for improper arguments
        if (! defined $class || defined $check) {

            # Global variable $axmud::CLIENT not set yet, so we'll just have to print the
            #   improper arguments message
            print "IMPROPER ARGUMENTS: Games::Axmud::Client->new " . join(' ', @_) . "\n";
            return undef;
        }

        # Set regexes to recognise URLs
        $urlRegex = 'http(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)'
                        . '([a-zA-Z0-9\-?\.\?\,\'\/\\\+&%\$#_\=\~]*)?';
        $shortRegex = '[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*\.(com|org|net|int|edu|gov|mil|io|uk)';
        # Set a regex to recognise email addresses
        $emailRegex = '\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b';

        # Setup
        my $self = {
            _objName                    => 'client',
            _objClass                   => $class,
            _parentFile                 => undef,       # No parent file object
            _parentWorld                => undef,       # No parent file object
            _privFlag                   => TRUE,        # All IVs are private

            # Perl object components
            # ----------------------

            # The main desktop object (GA::Obj::Desktop), which arranges windows on the desktop
            #   across one or more workspaces
            desktopObj                  => undef,

            # An IV which stores a 'main' window. First set when Axmud starts, and a spare 'main'
            #   window, not belonging to any session, opens before the Connections window opens
            # Briefly set back to 'undef' when the spare 'main' window is destroyed, just before a
            #   new 'main' window for a new session is created to replace it
            # Then set whenever $self->currentSession is set
            mainWin                     => undef,

            # The About window (only one can be open at a time)
            aboutWin                    => undef,       # Set by $self->set_aboutWin
            # A 'dialogue' window created by a call to GA::Generic::Win->showBusyWin, e.g. the
            #   'Loading...' window created by $self->start
            busyWin                     => undef,       # Set by $self->set_busyWin
            # The Connections window (only one can be open at a time)
            connectWin                  => undef,       # Set by $self->set_connectWin
            # The Client Console window (only one can be open at a time)
            consoleWin                  => undef,       # Set by $self->set_consoleWin

            # Instance variable constants
            # ---------------------------

            # All of Axmud's Perl objects have instance variables (IVs); the vast majority of them
            #   are either scalar IVs (including references stored in scalar variables), list IVs or
            #   hash IVs. There are a few more complex structures (e.g. arrays of arrays, hashes of
            #   arrays, etc), but besides that, there no other data structures (globs, etc)
            #
            # All objects have five standard 'constant' IVs which should not be modified, namely
            #   ->_objName, ->_objClass, ->_parentFile, ->_parentWorld and ->privFlag.
            # ->_objName gives a name to an object, even if it doesn't have a ->name IV.
            #   ->_objName is not necessarily unique
            # ->_objectClass is the same as the package name for the object (e.g. GA::Client)
            # ->_parentFile matches a key in the file object registry, $self->fileObjHash (see
            #   further below). It tells us which data file is used to save this object. (A few
            #   objects don't have a parent file; in these casees, ->_parentFile is set to 'undef')
            # ->_parentWorld is the name of the current world profile, in the GA::Session to which
            #   the object belongs. (Some objects don't belong to a particular GA::Session; in these
            #   cases, ->_parentWorld is set to 'undef')
            # ->_privFlag is set to TRUE if the object's IVs are private; FALSE if they are public.
            #   (See the comments at the top of generic_obj.pm)
            #
            # In addition, any IV whose name begins with 'const' is treated as a constant value
            #   which should not be modified
            #
            # The generic object, Games::Axmud, from which all Axmud objects inherit, provides
            #   several useful functions for accessing and storing values for all IVs, such as
            #   ->ivPush, ->ivSplice and ->ivPeek
            # The most important one is ->ivPoke. When an IV's value (or values) are set, it should
            #   not be done with a call to
            #       $self->{iv_name} = $value
            #   but with a call to
            #       $self->ivPoke('iv_name', $value)
            # ->ivPoke (like all the generic object's IV functions) tells the equivalent
            #   GA::Obj::File that its data has been modified, and that it needs to be saved
            # ->ivPoke, etc, can't be used to modify constant IVs (the five mentioned above, and any
            #   IV whose names starts 'const'
            #
            # Constant registry hash of constant instance variables (IVs) required by every Perl
            #   object. A hash in the form
            #   $constIVHash{iv_name} = undef
            constIVHash                 => {
                '_objName'              => undef,
                '_objClass'             => undef,
                '_parentFile'           => undef,
                '_parentWorld'          => undef,
                '_privFlag'             => undef,
            },
            # Constant registry hash of reserved names that can't be used by profiles and other
            #   Perl objects which have unique names (case-insensitive; these values never change)
            # Hash in the form
            #   $constReservedHash{string} = undef
            # NB Plurals have been added only when necessary, specifically because there's a file
            #   object with a name in the plural
            constReservedHash           => {
                'about'                 => undef,
                'alias'                 => undef,
                'amud'                  => undef,
                'axmud'                 => undef,
                'automap'               => undef,
                'automapper'            => undef,
                'buffer'                => undef,
                'cage'                  => undef,
                'client'                => undef,
                'cmd'                   => undef,
                'colour'                => undef,
                'color'                 => undef,   # American too
                'component'             => undef,
                'config'                => undef,
                'connect'               => undef,
                'console'               => undef,
                'contact'               => undef,
                'contacts'              => undef,
                'current'               => undef,
                'custom'                => undef,
                'default'               => undef,
                'defn'                  => undef,
                'definition'            => undef,
                'delete'                => undef,
                'desktop'               => undef,
                'dict'                  => undef,
                'dictionary'            => undef,
                'dicts'                 => undef,
                'edit'                  => undef,
                'exit'                  => undef,
                'external'              => undef,
                'favourite'             => undef,
                'favorite'              => undef,   # American too
                'file'                  => undef,
                'fixed'                 => undef,
                'flag'                  => undef,
                'free'                  => undef,
                'gauge'                 => undef,
                'generic'               => undef,
                'grid'                  => undef,
                'gui'                   => undef,
                'hash'                  => undef,
                'help'                  => undef,
                'hook'                  => undef,
                'icon'                  => undef,
                'init'                  => undef,
                'interface'             => undef,
                'internal'              => undef,
                'jmud'                  => undef,   # Old JMud plugin for KildClient
                'keycode'               => undef,
                'keycodes'              => undef,
                'label'                 => undef,
                'link'                  => undef,
                'list'                  => undef,
                'load'                  => undef,
                'localhost'             => undef,
                'login'                 => undef,
                'loop'                  => undef,
                'macro'                 => undef,
                'main'                  => undef,
                'map'                   => undef,
                'misc'                  => undef,
                'mission'               => undef,
                'model'                 => undef,
                'name'                  => undef,
                'node'                  => undef,
                'not_applicable'        => undef,   # Used in data files to represent assoc'd world
                'obj'                   => undef,
                'other'                 => undef,
                'otherdefn'             => undef,
                'otherprof'             => undef,
                'package'               => undef,
                'phrasebook'            => undef,
                'plugin'                => undef,
                'pref'                  => undef,
                'preference'            => undef,
                'profile'               => undef,
                'pseudo'                => undef,
                'quest'                 => undef,
                'region'                => undef,
                'regionmap'             => undef,
                'regionpath'            => undef,
                'room'                  => undef,
                'save'                  => undef,
                'scalar'                => undef,

lib/Games/Axmud/Client.pm  view on Meta::CPAN

                'kp_subtract'           => chr(27) . 'OS',      # PF4
            },
            # Constant hash of Axmud keycodes which must be intercepted by a ->signal_connect in
            #   GA::Win::Internal->setKeyPressEvent and sent to the world, whenever a session's
            #   special echo mode is enabled
            constDirectSpecialKeysHash  => {
                'escape'                => chr(27),
                'tab'                   => chr(11),
                'backspace'             => chr(8),
                'delete'                => chr(127),
            },

            # Debugging flag set to TRUE if invalid escape sequences should be displayed as 'debug'
            #   messages, FALSE otherwise
            debugEscSequenceFlag        => FALSE,       # [config]
            # Debugging flag set to TRUE if incoming option negotiations should be displayed as
            #   'debug' messages, FALSE otherwise
            debugTelnetFlag             => FALSE,       # [config]
            # Parallel debugging flag set to TRUE if a very short 'debug' message should be
            #   displayed for incoming option negotiations (ignored if ->debugTelnetFlag is TRUE)
            debugTelnetMiniFlag         => FALSE,       # [config]
            # Debugging flag set to TRUE if GA::Obj::Telnet should write its own logfile for
            #   option negotiations, telopt.log (store in Axmud's base directory)
            debugTelnetLogFlag          => FALSE,       # [config]
            # Debugging flag set to TRUE if MSDP data sent to Status/Locator tasks should be
            #   displayed as 'debug' messages, FALSE otherwise
            debugMsdpFlag               => FALSE,       # [config]
            # Debugging flag set to TRUE if MXP errors should be displayed as 'debug' messages,
            #   FALSE otherwise
            debugMxpFlag                => FALSE,       # [config]
            # Debugging flag set to TRUE if MXP comment elements should be displayed as 'debug'
            #   messages, FALSE otherwise
            debugMxpCommentFlag         => FALSE,       # [config]
            # Debugging flag set to TRUE if Pueblo errors should be displayed as 'debug' messages,
            #   FALSE otherwise
            debugPuebloFlag             => FALSE,       # [config]
            # Debugging flag set to TRUE if Pueblo comment elements should be displayed as 'debug'
            #   messages, FALSE otherwise
            debugPuebloCommentFlag      => FALSE,       # [config]
            # Debugging flag set to TRUE if ZMP data should be displayed as 'debug' messages, FALSE
            #   otherwise
            debugZmpFlag                => FALSE,       # [config]
            # Debugging flag set to TRUE if incoming ATCP data should be displayed as 'debug'
            #   messages, FALSE otherwise
            debugAtcpFlag               => FALSE,       # [config]
            # Debugging flag set to TRUE if incoming GMCP data should be displayed as 'debug'
            #   messages, FALSE otherwise
            debugGmcpFlag               => FALSE,       # [config]
            # Debugging flag set to TRUE if MCP errors should be displayed as 'debug' messages,
            #   FALSE otherwise
            debugMcpFlag                => FALSE,       # [config]

            # Desktop and display settings
            # ----------------------------

            # Display variables
            # Axmud makes use of a number of windows, both those it creates and 'external' windows
            #   it doesn't create
            # 'grid' windows are permanent (or semi-permanent). Axmud tries to arrange these windows
            #   so they don't overlap or, if there isn't enough room on the workspace, to make them
            #   overlap in sensible ways. Axmud tries to take account of the different desktop
            #   environments in getting the positioning of its windows right. 'grid' windows can
            #   include 'external' windows, such as media players, so that they don't overlap
            #   Axmud's own windows (if required)
            # 'free' windows are temporary, and are displayed in the middle of the workspace without
            #   regard for the positioning of any 'grid' windows
            # If your system has multiple workspaces available, 'grid' windows can be arranged on
            #   each one of them. Each workspace can have one or more 'workspace grids' on which
            #   'grid' windows for that workspace are arranged
            #
            # NB Recent Axmud versions can draw task windows and the Automapper window inside the
            #   'main' window, as well as in separate 'grid' windows, as before. These are called
            #   'pseudo-windows'. Their ->number IV is always -1, and they don't appear in registry
            #   hashes of windows. This feature is experimental, so in general comments don't
            #   mention them
            #
            # There are two modes for handling windows in multiple sessions, according to the
            #   setting of this flag:
            #       TRUE (traditional) : All sessions share a single 'main' window. The 'main'
            #           window's Gtk3::Grid contains one table object, a pane object
            #           (GA::Table::Pane) which displays text received from each session's world
            #           in a separate tab. No other widgets are allowed on the table. Other
            #           'internal' windows may be visible; on each workspace, they are arranged on
            #           workspace grids belonging to their session (on every workspace, one
            #           workspace grid for every session). The current session is the one whose tab
            #           is visible; only its 'internal' windows are visible; 'internal' windows
            #           controlled by other sessions are not visible (usually minimised)
            #       FALSE: All sessions have their own 'main' window. The 'main' window's
            #           Gtk3::Grid contains at least one pane object, but can also contain other
            #           table widgets (including other pane objects). Other 'internal' windows are
            #           arranged on a workspace grid shared by all sessions (one workspace grid for
            #           every workspace in use). When the current session changes, no windows are
            #           made visible or invisible (usually minimised or un-minimised)
            shareMainWinFlag            => TRUE,            # [config]
            # When the user wants to change the setting of ->shareMainWinFlag, the change can't be
            #   applied immediately.
            # Instead, this IV is set to 'on' or 'off'. When Axmud next starts, if this IV is not
            #   set to 'default', ->shareMainWinFlag is set to TRUE (for 'on') or FALSE (for 'off');
            #   ->restartShareMainWinMode is then set back to 'default'
            restartShareMainWinMode     => 'default',       # [config]
            # Workspace grids can be available, or not. GA::Obj::Desktop->gridPermitFlag is set to
            #   FALSE if workspace grids are not available at all (because the desktop is too small,
            #   because Axmud is running on MS Windows or running in blind mode, etc)
            # Independent of that flag is this one, which the user can set with
            #   ';activategrid' and ';disactivategrid'. When Axmud starts, it tries to create
            #   workspace grids if this flag and GA::Obj::Desktop->gridPermitFlag are both TRUE
            activateGridFlag            => TRUE,            # [config]
            # When workspace grids are not available, Axmud can try to remember the size and
            #   position of its 'grid' windows. Users can adjust the size/positions manually, and
            #   have those settings applied in future sessions
            # Flag set to TRUE if Axmud should try to remember the size and position of 'grid'
            #   windows, FALSE if not (can be TRUE even if workspace grids are available)
            storeGridPosnFlag           => FALSE,           # [config]
            # When ->storeGridPosnFlag is TRUE, Axmud uses this hash to store the size/position of
            #   each 'grid' window with a unique ->winName (e.g. 'main', 'status_task', 'map').
            #   When it's FALSE, this hash is not updated at all
            # A key-value pair is created when a 'grid' window is created, if there's not already a
            #   matching entry in the hash
            # If a 'grid' window is created when workspace grids are not available, and if there's
            #   an entry in this hash with the same ->winName, that entry is used for the size and
            #   position of the new 'grid' window. If, on the other hand, workspace grids are
            #   available, then the new window's size and position is set by the workspace grid code
            # If a 'grid' window is resized (by code or manually by the user) and it's either the
            #   only window with the same ->winName open, or the first one of that ->winName that
            #   was opened, then the entry in this hash is updated
            # 'External' windows are never added to this hash
            # Hash in the form
            #   ->storeGridPosnHash{window_name} = list_reference
            # ...where 'list_reference' is a reference to a list in the form (x y width height). At
            #   least one of the four values must be defined. When a 'grid' window is created and
            #   workspace grids aren't available, the posn is only used if both 'x' and 'y' are
            #   defined, and the size is only used if both 'width' and 'height' are defined. Those
            #   are the minimum requirements; in actual use, Axmud's code almost certainly delivers
            #   an entry with all four values defined
            storeGridPosnHash           => {},              # [config]
            #
            # Constant hash of standard 'grid' window types (any type of window that can be put onto
            #   a workspace grid; includes 'external' windows, but doesn't include the data viewer
            #   window, 'edit' windows, 'pref' windows, 'dialogue' windows etc)
            # NB 'Internal' windows are a sub-class of 'grid' window handled by GA::Win::Internal,
            #   consisting of the window types 'main', 'protocol' and 'custom'
            # Hash in the form
            #   $constWinTypeHash{window_type} = 'undef'
            # ...where 'window_type' matches GA::Generic::GridWin->winType
            constGridWinTypeHash        => {
                # Any 'grid' window used as a 'main' window by any session (can be created only by
                #   GA::Client or GA::Session)
                'main'                  => undef,
                # Any 'grid' window used as an Automapper window (can be created by any code)
                'map'                   => undef,
                # Any 'grid' window created by a MUD protocol such as MXP (can be created only by
                #   GA::Session)
                'protocol'              => undef,
                # Any 'grid' window NOT used as a 'main' window by any session (besides 'map' and
                #   'protocol' windows), whose widgets can't be customised (can be created by any
                #   code)
                'fixed'                 => undef,
                # Any 'grid' window NOT used as a 'main' window by any session (besides 'map' and
                #   'protocol' windows), whose widgets can be customised (e.g. windows controlled
                #    by individual tasks; can be created by any code)
                'custom'                => undef,
                # Any 'external' window (not created by Axmud, but which has been placed on a

lib/Games/Axmud/Client.pm  view on Meta::CPAN

            return undef;
        }

        $dir = $axmud::SHARE_DIR . '/items/sounds/';
        if (! -e $dir) {

            return undef;
        }

        # Get a list of text files in this directory
        if (! opendir ($dirHandle, $dir)) {

            return undef;

        } else {

            @fileList = readdir ($dirHandle);
            closedir $dirHandle;
        }

        # Filter out non-sound files
        $regex = '\.(' . join('|', $self->ivKeys('constSoundFormatHash')) . ')$';
        foreach my $file (@fileList) {

            my $name;

            if ($file =~ m/$regex/) {

                $name = $file;
                $name =~ s/$regex//;

                $hash{$name} = $file;
            }
        }

        # Set the IV
        $self->{constExtendedSoundHash} = \%hash;

        return 1;
    }

    # Start / stop

    sub start {

        # Called by axmud.pl on startup, immediately after the call to GA::Client->new
        # Starts the client. Opens the first 'main' window, and prompts the user to connect to a
        #   world
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if setup fails
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my (
            $warningFlag, $roomObj, $exitObj, $desktopObj, $host, $engine, $port, $world, $profObj,
            $taskObj, $offlineFlag,
            @list,
        );

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->start', @_);
        }

        # Set the date/time at which the client started
        $self->ivPoke('startTime', $self->localTime());
        $self->ivPoke('startClock', $self->localClock());
        $self->ivPoke('startDate', $self->localDate());
        $self->ivPoke('startClockString', $self->localClockString());
        $self->ivPoke('startDateString', $self->localDateString());

        # In Axmud test mode, blind mode is always disabled
        if ($axmud::TEST_MODE_FLAG) {

            $axmud::BLIND_MODE_FLAG = FALSE;
        }

        # In Axmud blind mode, TTS is always enabled
        if ($axmud::BLIND_MODE_FLAG) {

            $self->ivPoke('systemAllowTTSFlag', TRUE);
        }

        # Load the basic mudlist, and store the data in GA::Obj::BasicWorld objects. The data
        #   isn't important, so don't disable loading/saving of data files if the operation fails
        if (! $self->loadBasicWorlds()) {

            $warningFlag = TRUE;
            $self->writeWarning(
                'Could not load basic mudlist (files possible corrupted)',
                $self->_objClass . '->start',
            );
        }

        # Load phrasebooks, and store the data in GA::Obj::PhraseBook objects. The data isn't
        #   important, so don't disable loading/saving of data files if the operation fails
        if (! $self->loadPhrasebooks()) {

            $warningFlag = TRUE;
            $self->writeWarning(
                'Could not load phrasebooks (files possible corrupted)',
                $self->_objClass . '->start',
            );
        }

        # Create the file objects stored by this GA::Client object
        if (! $self->createFileObjs()) {

            $warningFlag = TRUE;
            $self->writeWarning(
                'Could not create file objects for the client - loading/saving disabled',
                $self->_objClass . '->start',
            );

lib/Games/Axmud/Client.pm  view on Meta::CPAN

        $self->setupKeycodes();
        # Create default TTS objects
        $self->ttsCreateStandard();

        # Create a room object and an exit object with default values for their IVs, used to supply
        #   default values for all other room/exit objects
        $roomObj = Games::Axmud::ModelObj::Room->new($self, 'default', 'global');
        $exitObj = Games::Axmud::Obj::Exit->new($self, 'default', 'global');
        if (! $roomObj || ! $exitObj) {

            return $self->writeError(
                'Could not initialise the default room and exit objects',
                $self->_objClass . '->start',
            );

        } else {

            $axmud::DEFAULT_ROOM = $roomObj;
            $axmud::DEFAULT_EXIT = $exitObj;
        }

        # Load (or create) the config file (if allowed)
        if (! $self->configFileObj->setupConfigFile()) {

            $warningFlag = TRUE;
            $self->writeWarning(
                'Error reading (or creating) the ' . $axmud::SCRIPT . ' config file - loading'
                . '/saving disabled',
                $self->_objClass . '->start',
            );

            # Disable all loading/saving of data files (the TRUE argument means 'don't prompt the
            #   user to do an emergency save')
            $self->disableAllFileAccess(TRUE);
        }

        # Any changes to IV values which could not be applied immediately, the last time Axmud was
        #   running, should be applied now
        # (->restartShareMainWinMode is 'default' if no change is required, or 'off' / 'on' if a
        #   change is required)
        if ($self->restartShareMainWinMode ne 'default') {

            if ($self->restartShareMainWinMode eq 'off') {
                $self->ivPoke('shareMainWinFlag', FALSE);
            } else {
                $self->ivPoke('shareMainWinFlag', TRUE);
            }

            $self->ivPoke('restartShareMainWinMode', 'default');
        }

        # Initialise the default set of toolbar button objects
        if (! $self->initialiseToolbar()) {

            return $self->writeError(
                'Could not initialise toolbar button objects',
                $self->_objClass . '->start',
            );
        }

        # Create the main desktop object. Set up the default workspace, set up window icons, prepare
        #   rc-file styles for each kind of window, create the first 'main' window
        $desktopObj = Games::Axmud::Obj::Desktop->new();
        if (! $desktopObj) {

            return $self->writeError(
                'Could not set up the desktop',
                $self->_objClass . '->start',
            );

        } else {

            $self->ivPoke('desktopObj', $desktopObj);
            if (! $desktopObj->start()) {

                return $self->writeError(
                    'Could not set up the desktop',
                    $self->_objClass . '->start',
                );

            } else {

                # Loading of data files and plugins could take some seconds; make the whole 'main'
                #   window visible in the meantime
                $desktopObj->updateWidgets($self->_objClass . '->start');
            }
        }

        # Perform an auto-backup of Axmud's data directory, if required
        if (
            $self->autoBackupMode eq 'all_start'
            || (
                $self->autoBackupMode eq 'interval_start' && $self->checkBackupInterval()
            )
        ) {
            $self->doAutoBackup();
        }

        # Display a 'dialogue' window while loading data files/plugins
        if (! $axmud::TEST_MODE_FLAG && ! $axmud::BLIND_MODE_FLAG) {

            $self->mainWin->showBusyWin();
        }

        # If the user is using a new version of Axmud, check if there are any new pre-configured
        #   worlds in this version. If so, insert them into Axmud's data directory
        if (
            $self->convertVersion($axmud::VERSION) > $self->convertVersion($self->prevClientVersion)
        ) {
            $self->insertPreConfigWorlds();
            # Don't perform this operation again until the next Axmud release
            $self->ivPoke('prevClientVersion', $axmud::VERSION);
        }

        # Load world profiles, creating a file object for each (if allowed, and if there are any to
        #   load)
        if ($self->loadDataFlag && $self->configWorldProfList && ! $axmud::TEST_PRE_CONFIG_FLAG) {

            if (! $self->loadWorldProfs() ) {

                $warningFlag = TRUE;
                $self->writeWarning(
                    'Error loading the world profiles expected after reading the \'config\''
                    . ' file',
                    $self->_objClass . '->start',
                );
            }
        }

        # Load data for the remaining file objects ('tasks', 'scripts', 'contacts', 'dicts',
        #   'toolbar', 'usercmds', 'zonemaps', 'winmaps', 'tts')
        if ($self->loadDataFlag && ! $self->loadOtherFiles()) {

            $warningFlag = TRUE;
            $self->writeWarning(
                'Error reading (or creating) data files - loading/saving disabled',
                $self->_objClass . '->start',
            );

            # Disable all loading/saving of data files (the TRUE argument means 'don't prompt the
            #   user to do an emergency save')
            $self->disableAllFileAccess(TRUE);
        }

        # Add remaining workspaces, if any are specified
        $desktopObj->setupWorkspaces();

        # Set up client commands
        if (! $self->setupCmds()) {

            # (Allow writing to something other than GA::Session - there are no sessions yet)
            return $self->writeError(
                'Could not initialise ' . $axmud::SCRIPT . ' client commands',
                $self->_objClass . '->start',
            );
        }

        # Delete the contents of log directories (leaving files inside worlds' own log directories
        #   intact), if the flag is set (but don't delete directories if this function has produced
        #   any warning messages - so that the messages aren't lost
        if ($self->deleteStandardLogsFlag && ! $warningFlag) {

            $self->deleteStandardLogs();
        }

        # Load plugins in the /private directory, if it exists (will not exist in any public release
        #   of Axmud)
        if (-e $axmud::SHARE_DIR . '/private') {

            $self->loadPrivatePlugins();
        }

        # Load initial plugins
        if ($self->initPluginList) {

            foreach my $pluginPath ($self->initPluginList) {

                if (! $self->loadPlugin($pluginPath)) {

                    $self->writeWarning(
                        'Error loading the plugin \'' . $pluginPath . '\'',
                        $self->_objClass . '->start',
                    );
                }
            }
        }

        # Close the 'dialogue' window and reset the Client IV that stores it
        if ($self->busyWin) {

            $self->mainWin->closeDialogueWin($self->busyWin);
        }

        # Start the client loop
        if (! $self->startClientLoop()) {

            return $self->writeError(
                'Could not start the client loop',
                $self->_objClass . '->start',
            );
        }

        # Prepare to initialise connections
        if ($self->showSetupWizWinFlag) {

            # When Axmud runs for the first time (specifically, when there is no Axmud config file)
            #   this flag will be set to TRUE, instructing us to open the Setup 'wiz' window so the
            #   new user can initialise a few settings
            if ($axmud::TEST_MODE_FLAG) {

                # In Axmud test mode, don't show the Setup window at all; just insert a couple of
                #   tasks into the global initial tasklist
                $self->addGlobalInitTask('status_task');
                $self->addGlobalInitTask('locator_task');

                # Don't show the setup window twice
                $self->set_showSetupWizWinFlag(FALSE);

            } elsif ($axmud::BLIND_MODE_FLAG) {

                # In Axmud blind mode, don't show the Setup window at all; instead, insert a few
                #   tasks into the global initial tasklist, and modify a few of their settings
                #   (specifically, none of them open a task window)
                $taskObj = $self->addGlobalInitTask('status_task');
                if ($taskObj) {

                    $taskObj->set_startWithWinFlag(FALSE);
                }

                $taskObj = $self->addGlobalInitTask('locator_task');
                if ($taskObj) {

                    $taskObj->set_startWithWinFlag(FALSE);
                }

                $taskObj = $self->addGlobalInitTask('compass_task');
                if ($taskObj) {

                    $taskObj->set_startWithWinFlag(FALSE);
                }

                $taskObj = $self->addGlobalInitTask('divert_task');
                if ($taskObj) {

                    $taskObj->set_requireWinFlag(FALSE);
                    $taskObj->set_startWithWinFlag(FALSE);
                    # Turn off sound effects, since TTS is used instead
                    $taskObj->ivUndef('tellAlertSound');
                    $taskObj->ivUndef('socialAlertSound');
                    $taskObj->ivUndef('customAlertSound');
                    $taskObj->ivUndef('warningAlertSound');
                    $taskObj->ivUndef('otherAlertSound');
                }

                # Don't show the setup window twice
                $self->set_showSetupWizWinFlag(FALSE);

            } else {

                # Open the setup window. When it closes, it will open the Connections window for us
                $self->mainWin->quickFreeWin('Games::Axmud::WizWin::Setup');

                # Disable most main window toolbar/menu items, while the window is open
                $self->desktopObj->restrictWidgets();

                # Don't show the setup window twice
                $self->set_showSetupWizWinFlag(FALSE);

                return 1;
            }
        }

        if (@ARGV) {

            # The user started Axmud with arguments, e.g. from a Linux terminal:
            #   ./axmud.pl deathmud.com 5000
            # If the port is not specified, the generic port is used:
            #   ./axmud.pl deathmud.com          (use port 23)
            # The user can also specify a world profile name:
            #   ./axmud.pl deathmud
            #
            # Any of those formats can use baxmud.pl rather than axmud.pl
            # Any of those formats can specify one of Axmud's supported text-to-speech engines,
            #   after all the other arguments (any of the items in $self->constTTSList, e.g.
            #   'festival'). If a recognised speech engine is specified, that engine is used for
            #   all text-to-speech while Axmud is running
            $engine = $ARGV[-1];
            if (defined $engine && defined $self->ivFind('constTTSList', $engine)) {

                pop @ARGV;
                $self->ivPoke('forceTTSEngine', $engine);
            }
        }

        if (@ARGV) {

            $host = shift @ARGV;
            $port = shift @ARGV;
            if (@ARGV) {

                # (Allow writing to something other than GA::Session - there are no sessions yet)
                return $self->writeError(
                    'Invalid command line arguments (try \'<host> <port>\', \'<host>\' or'
                    . ' \'<world_name>\')',
                    $self->_objClass . '->start',
                );
            }

            if (! $port && $self->ivExists('worldProfHash', $host)) {

                # $host is a world profile name
                $world = $host;
                $profObj = $self->ivShow('worldProfHash', $world);
                ($host, $port) = $profObj->getConnectDetails();

                if (! $self->startSession($world, $host, $port)) {

                    # (Allow writing to something other than GA::Session - there are no sessions
                    #   yet)
                    return $self->writeError(
                        'Could not open a session connecting to \'' . $host . '\'',
                        $self->_objClass . '->start',
                    );
                }

lib/Games/Axmud/Client.pm  view on Meta::CPAN


             return $axmud::CLIENT->writeImproper($self->_objClass . '->stop', @_);
        }

        # The END() function will call this function again, before terminating the script, unless
        #   we set this flag
        # (The TRUE value also gives GA::Obj::WorkspaceGrid->stop to destroy a shared 'main'
        #   window, rather than just disengaging it)
        $self->ivPoke('shutdownFlag', TRUE);

        # This flag prevents multiple concurrent calls to this function if, for example, the user
        #   is repeatedly clicking the 'main' window's close button
        if ($self->terminatingFlag) {

            return undef;

        } else {

            $self->ivPoke('terminatingFlag', TRUE);
        }

        # If any text is being convertd to speech right now, interrupt it
        $self->ttsInterruptJob();

        # Perform an auto-backup of Axmud's data directory, if required
        if (
            $self->autoBackupMode eq 'all_stop'
            || (
                $self->autoBackupMode eq 'interval_stop' && $self->checkBackupInterval()
            )
        ) {
            $self->doAutoBackup();
        }

        # Fire any hooks in any session that are using the 'close_disconnect' hook event
        foreach my $sessionObj ($self->listSessions()) {

            $sessionObj->checkHooks('close_disconnect');
        }

        # Stop every current session
        if ($self->sessionHash && ! $self->stopAllSessions()) {

            return $self->writeError(
                'Could not stop all sessions',
                $self->_objClass . '->stop',
            );
        }

        # Stop the client loop (if running)
        if ($self->clientLoopObj && ! $self->stopClientLoop()) {

            return $self->writeError(
                'Could not stop the client loop',
                $self->_objClass . '->stop',
            );
        }

        # Close any remaining 'internal' windows, restore 'external' windows to their original size/
        #   position, close any remaining 'free' windows
        if (! $self->desktopObj->stop()) {

            return $self->writeError(
                'Could not stop the desktop object',
                $self->_objClass . '->stop',
            );
        }

        # Delete everything in the temporary directories
        # (The former is for plugins; the latter is for Axmud code)
        foreach my $tempDir ($axmud::DATA_DIR . '/tmp/', $axmud::DATA_DIR . '/data/temp/') {

            # Simplest way to empty the directory and all its sub-directories seems to be to
            #   destroy the directory and make a new one
            File::Path::remove_tree($tempDir);
            mkdir ($tempDir, 0755);
        }

        # Halt Gtk3, which halts Axmud
        Gtk3->main_quit();

        return 1;
    }

    # Setup

    sub loadBasicWorlds {

        # Called by $self->start
        # Loads data from the basic mudlist and stores it in GA::Obj::BasicWorld objects, ready for
        #   the Connections window to display
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments, if the file can't be read or if it appears to be
        #       corrupted
        #   1 on success

        my ($self, $check) = @_;

        # Local variables
        my (
            $path, $fileHandle, $exitFlag,
            @list,
            %hash,
        );

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->loadBasicWorlds', @_);
        }

        $path = $axmud::SHARE_DIR . '/items/mudlist/mudlist.txt';
        if (! -e $path) {

            return undef;
        }

        # Open the file for reading
        if (! open ($fileHandle, "<$path")) {

lib/Games/Axmud/Client.pm  view on Meta::CPAN

        do {

            $count++;
            if (! $self->ivExists('zonemapHash', 'temp_' . $count)) {

                $name = 'temp_' . $count;
            }

        } until ($name);

        # Unlikely in the extreme that we'll ever use a trillion zonemaps, but let's check anyway
        if (! $self->nameCheck($name, 16)) {

            return undef;
        }

        # Create the temporary zonemap
        $zonemapObj = Games::Axmud::Obj::Zonemap->new($name, TRUE, $session);
        if (! $zonemapObj) {

            return undef;
        }

        # Add the zonemap to the client's registries
        $self->ivAdd('zonemapHash', $zonemapObj->name, $zonemapObj);

        # Add a single zone model, covering the whole of the zonemap's internal grid
        $modelObj = Games::Axmud::Obj::ZoneModel->new($zonemapObj);
        $modelObj->{left} = 0;
        $modelObj->{right} = 59;
        $modelObj->{top} = 0;
        $modelObj->{bottom} = 59;
        $modelObj->{width} = 60;
        $modelObj->{height} = 60;
        $zonemapObj->addZoneModel($modelObj);

        return $zonemapObj;
    }

    sub checkCurrentZonemap {

        # Called by GA::EditWin::ZoneModel and GA::Cmd::ModifyZoneModel->do
        # Checks whether the specified zonemap is being used by any workspace grid (a
        #   GA::Obj::WorkspaceGrid object), because a zonemap in use cannot be modified
        #
        # Expected arguments
        #   $zonemap    - The name of the zonemap to check
        #
        # Return values
        #   'undef' in improper arguments or if $zonemap isn't in use by any workspace grid
        #   1 if $zonemap is in use by any workspace grid

        my ($self, $zonemap, $check) = @_;

        # Check for improper arguments
        if (! defined $zonemap || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->checkCurrentZonemap', @_);
        }

        foreach my $workspaceGridObj ($self->desktopObj->ivValues('gridHash')) {

            if ($workspaceGridObj->zonemap && $workspaceGridObj->zonemap eq $zonemap) {

                return 1;
            }
        }

        # $zonemap isn't in use by any workspace grid
        return undef;
    }

    sub createStandardWinmaps {

        # Called by $self->start
        # Makes sure the standard GA::Obj::Winmap objects specified by
        #   $self->constWinmapNameHash (e.g. 'main_fill', 'entry_empty') exist and, if not, create
        #   them
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments or if one of the standard winmaps doesn't exist and can't
        #       be created
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my $modFlag;

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->createStandardWinmaps', @_);
        }

        foreach my $winmapName ($self->ivKeys('constWinmapNameHash')) {

            my $winmapObj;

            # If this standard winmap hasn't already been created...
            if (! $self->ivExists('winmapHash', $winmapName)) {

                # ...then create it
                $winmapObj = Games::Axmud::Obj::Winmap->new($winmapName);
                if (! $winmapObj) {

                    return $self->writeError(
                        'Cannot set up the standard \'' . $winmapName . '\' winmap',
                        $self->_objClass . '->createStandardWinmaps',
                    );

                } else {

                    # Add the winmap to the client's registries
                    $self->ivAdd('winmapHash', $winmapObj->name, $winmapObj);
                    $self->ivAdd('standardWinmapHash', $winmapObj->name, $winmapObj);

                    $modFlag = TRUE;

lib/Games/Axmud/Client.pm  view on Meta::CPAN


                        # Create a new toolbar button object
                        my $obj = Games::Axmud::Obj::Toolbar->new(
                            $name,
                            $descrip,
                            FALSE,          # Default, not custom, toolbar button
                            $iconPath,
                            $instruct,
                            $sessionFlag,
                            $connectFlag,
                        );

                        if (! $obj) {

                            return undef;

                        } else {

                            $self->ivAdd('toolbarHash', $obj->name, $obj);
                            $self->ivPush('toolbarList', $obj->name);
                        }
                    }
                }

            } until (! @defaultList);
        }

        return 1;
    }

    sub connectBlind {

        # Called by $self->start in Axmud blind mode
        # Open a series of standard 'dialogue' windows, allowing the visually-impaired user to
        #   select a world and/or character
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 on success

        my ($self, $check) = @_;

        # Local variables
        my (
            $newWorldString, $newCharString, $title, $choice, $connectWorld, $connectWorldObj,
            $connectHost, $connectPort, $loginMode, $connectChar, $connectPwd, $connectAccount,
            $startFlag,
            @faveList, @visitedList, @otherList, @comboList, @comboList2, @comboList3,
            %worldHash, %nameHash, %checkHash, %loginHash,
        );

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->connectBlind', @_);
        }

        # Make sure the test window for finding desktop panels has been removed
        $self->desktopObj->updateWidgets($self->_objClass . '->connectBlind');

        # Get a list of worlds, favourite worlds first, then all visited worlds sorted by number
        #   of visits, finally unvisited worlds sorted alphabetically
        # (Code borrowed from GA::OtherWin::Connect->resetTreeView)
        $newWorldString = 'Create new world';
        $newCharString = 'Create new character';

        # For each world, decide which name to use. Create a hash in the form
        #   $nameHash{profile_name} = displayed_name
        #   (where 'displayed_name' is the long name, if available, or the profile name, if not
        # At the same time, create a parallel hash to check for duplicate long names, in the form
        #   $checkHash{long_name} = profile_name
        # If duplicate long names are found, 'displayed_name' should include both the long name and
        #   the profile name
        %worldHash = $self->worldProfHash;
        foreach my $worldObj (values %worldHash) {

            my ($otherWorld, $otherWorldObj);

            if ($worldObj->longName) {

                if ($worldObj->longName eq $newWorldString) {

                    $nameHash{$worldObj->name} = $worldObj->longName . ' (' . $worldObj->name . ')';
                    $checkHash{$worldObj->longName} = $worldObj->name;

                } elsif (exists $checkHash{$worldObj->longName}) {

                    # Amend both entries to include the long name and the profile name
                    $otherWorld = $checkHash{$worldObj->longName};
                    $otherWorldObj = $worldHash{$otherWorld};

                    $nameHash{$worldObj->name} = $worldObj->longName . ' (' . $worldObj->name . ')';
                    # (There's already an entry in $checkHash matching ->longName)
                    $nameHash{$otherWorld}
                        = $otherWorldObj->longName . ' (' . $otherWorldObj->name . ')';

                } else {

                    # Not a duplicate, so just display the long name
                    $nameHash{$worldObj->name} = $worldObj->longName;
                    $checkHash{$worldObj->longName} = $worldObj->name;
                }

            } else {

                # Just display the profile name
                $nameHash{$worldObj->name} = $worldObj->name;
            }
        }

        # Remove all favourite worlds from %worldHash, so they can be displayed first
        foreach my $name ($self->favouriteWorldList) {

            if (exists $worldHash{$name}) {

                push (@faveList, $worldHash{$name});
                delete $worldHash{$name};
            }
        }

lib/Games/Axmud/Client.pm  view on Meta::CPAN


            return undef;
        }

        # Stop the loop
        $result = $self->clientLoopObj->stopLoop();

        # Update IVs
        $self->ivUndef('clientLoopObj');

        return $result;
    }

    sub spinClientLoop {

        # Called by $self->clientLoopObj->spinLoop when the client loop spins
        # Updates 'internal' windows and some of their strip objects. Makes blinking text in
        #   Gtk3::Textviews blink on or off. Checks file objects and, if any need to be saved,
        #   updates the title of 'main' windows
        #
        # Expected arguments
        #   $loopObj    - The GA::Obj::Loop object handling the client loop
        #
        # Return values
        #   'undef' on improper arguments, if the client loop isn't running or if $loopObj is the
        #       wrong loop object
        #   1 otherwise

        my ($self, $loopObj, $check) = @_;

        # Local variables
        my (
            $connectInfoFlag, $result,
            @bufferList,
        );

        # Check for improper arguments
        if (! defined $loopObj || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->spinClientLoop', @_);
        }

        if (
            ! $self->clientLoopObj
            || $self->clientLoopObj ne $loopObj
            || $self->suspendSessionLoopFlag
        ) {
            return undef;
        }

        # Update IVs
        $self->ivPoke('clientTime', $loopObj->spinTime);

        # Update blinkers states for each session
        foreach my $session ($self->listSessions()) {

            $session->updateBlinkers();
        }

        # Update 'internal' windows
        foreach my $winObj ($self->desktopObj->listGridWins()) {

            my (
                $stripObj,
                %hash,
            );

            if (
                $winObj->winType eq 'main'
                || $winObj->winType eq 'protocol'
                || $winObj->winType eq 'custom'
            ) {
                # If the GA::Strip::GaugeBox strip object's gauge box has been empty for too long,
                #   remove it
                $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::GaugeBox');
                if (
                    $stripObj
                    && defined $stripObj->gaugeCheckTime
                    && $stripObj->gaugeCheckTime < $self->clientTime
                ) {
                    $stripObj->removeGaugeBox();
                }

                # If the GA::Strip::Entry strip object's console button is in flashing mode, make
                #   it flash once
                if ($winObj->visibleSession && $winObj->visibleSession->systemMsgCheckTime) {

                    $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
                    if ($stripObj) {

                        $stripObj->set_consoleIconFlash();
                    }
                }

                # Update the GA::Strip::ConnectInfo strip object in every 'internal' window
                #   approximately every second
                # (GA::Strip::ConnectInfo are allowed in 'internal' windows besides 'main' windows,
                #   but they don't display connection data)
                if ($winObj->visibleSession) {

                    if (
                        ! $winObj->visibleSession->connectInfoCheckTime
                        || $winObj->visibleSession->connectInfoCheckTime
                                < int($winObj->visibleSession->sessionTime)
                    ) {
                        $winObj->setTimeLabel($winObj->visibleSession->getTimeLabelText());
                    }

                } else {

                    $winObj->setTimeLabel('');
                }

                # Re-draw blinkers in any 'internal' window which uses them, as appropriate
                # (GA::Strip::ConnectInfo are allowed in 'internal' windows besides 'main' windows,
                #   but blinkers are only updated in the session's 'main' window)
                if ($winObj->visibleSession) {

                    $stripObj
                        = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::ConnectInfo');

                    if ($stripObj) {

                        # Import the visible session's blinker state hash (for convenience)
                        %hash = $winObj->visibleSession->blinkerStateHash;

                        # Redraw each blinker that is off, but should be on (and vice-versa)
                        foreach my $blinkerNum (keys %hash) {

                            my ($blinkerState, $blinkerObj);

                            $blinkerState = $hash{$blinkerNum};
                            $blinkerObj = $stripObj->ivShow('blinkerHash', $blinkerNum);

                            if (
                                $blinkerObj
                                && (
                                    (defined $blinkerState && ! $blinkerObj->onFlag)
                                    || (! defined $blinkerState && $blinkerObj->onFlag)
                                )
                            ) {
                                # Redraw this blinker
                                $stripObj->drawBlinker($blinkerNum, ! ($blinkerObj->onFlag));
                            }
                        }
                    }
                }
            }
        }

        # Get a list of textview buffers which might contain blinking text
        foreach my $textViewObj ($self->desktopObj->ivValues('textViewHash')) {

            push (@bufferList, $textViewObj->buffer);
        }

        # Update the buffers' text tags (if it's time to do so), making the text appear or disappear
        #   on cue
        if ($self->clientTime > $self->blinkSlowCheckTime) {

            foreach my $buffer (@bufferList) {

                my ($blinkTag, $cursorTag);

                $blinkTag = $buffer->get_tag_table->lookup('blink_slow');
                $cursorTag = $buffer->get_tag_table->lookup('cursor');

                if ($blinkTag) {

                    $blinkTag->set_property('foreground-set', $self->blinkSlowFlag);
                    $blinkTag->set_property('background-set', $self->blinkSlowFlag);
                }

                # The textview object's cursor is updated at the same time
                if ($cursorTag && ! $self->useFastCursorFlag) {

                    if ($self->blinkSlowFlag) {
                        $cursorTag->set_property('underline', 'single');
                    } else {
                        $cursorTag->set_property('underline', 'none');
                    }
                }
            }

            if ($self->blinkSlowFlag) {
                $self->ivPoke('blinkSlowFlag', FALSE);
            } else {
                $self->ivPoke('blinkSlowFlag', TRUE);
            }

            $self->ivPoke('blinkSlowCheckTime', $self->clientTime + ($self->blinkSlowTime / 2));
        }

        if ($self->clientTime > $self->blinkFastCheckTime) {

            foreach my $buffer (@bufferList) {

                my ($blinkTag, $cursorTag);

                $blinkTag = $buffer->get_tag_table->lookup('blink_fast');
                $cursorTag = $buffer->get_tag_table->lookup('cursor');

                if ($blinkTag) {

                    $blinkTag->set_property('foreground-set', $self->blinkFastFlag);
                    $blinkTag->set_property('background-set', $self->blinkFastFlag);
                }

                # The textview object's cursor is updated at the same time
                if ($cursorTag && $self->useFastCursorFlag) {

                    if ($self->blinkFastFlag) {

lib/Games/Axmud/Client.pm  view on Meta::CPAN

        # Restarts client/session loops stopped by a call to $self->haltSessionLoops
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->restoreSessionLoops', @_);
        }

        $self->ivPoke('suspendSessionLoopFlag', FALSE);

        # The Glib::Timeout might have stopped, or not (depending on what caused the error), so we
        #   need to formally stop it
        foreach my $session ($self->ivValues('sessionHash')) {

            $session->startSessionLoop();
        }

        return 1;
    }

    sub checkMainWinTitles {

        # Called by $self->spinClientLoop
        # This titles of all 'main' windows need to be changed from time to time (all 'main' windows
        #   have the same title, and any changes are performed on all 'main' windows at the same
        #   time)
        # This function checks whether it's necessary to change them and performs the operation, if
        #   so
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my (
            $exitFlag,
            @winObjList,
        );

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->checkMainWinTitles', @_);
        }

        # Get an ordered list of all 'main' windows
        @winObjList = $self->desktopObj->listGridWins('main');

        # Check file objects for the client
        if ($self->showModFlag) {

            # 'main' window titles contain an asterisk, meaning that some files need to be saved. If
            #   they have all been saved, we change the titles
            OUTER: foreach my $fileObj ($self->ivValues('fileObjHash')) {

                if ($fileObj->modifyFlag) {

                    $exitFlag = TRUE;
                    last OUTER;
                }
            }

            if (! $exitFlag) {

                # All files already saved; change 'main' windows titles
                foreach my $winObj (@winObjList) {

                    $winObj->setMainWinTitle(FALSE);
                }

                # Update IVs
                $self->ivPoke('showModFlag', FALSE);
            }

        } else {

            # 'main' window titles don't contain an asterisk, meaning that no files need to be
            #   saved. If any of them now need to be saved, we change the titles
            OUTER: foreach my $fileObj ($self->ivValues('fileObjHash')) {

                if ($fileObj->modifyFlag) {

                    $exitFlag = TRUE;
                    last OUTER;
                }
            }

            if ($exitFlag) {

                # At least one file needs to be saved; change the label (the window's ->showModFlag
                #   is automatically updated)
                # All files already saved; change 'main' window titles
                foreach my $winObj (@winObjList) {

                    $winObj->setMainWinTitle(TRUE);
                }

                # Update IVs
                $self->ivPoke('showModFlag', TRUE);
            }
        }

        return 1;
    }

    sub paneModifyBorder {

lib/Games/Axmud/Client.pm  view on Meta::CPAN


        # Immediately restore the border size of any other pane object in this window
        foreach my $time ($self->ivKeys('paneRestoreHash')) {

            my $otherPaneObj = $self->ivShow('paneRestoreHash', $time);

            if ($otherPaneObj->winObj && $otherPaneObj->winObj eq $paneObj->winObj) {

                # Restore this pane object's border size immediately
                $otherPaneObj->set_borderWidth(FALSE);
                $self->ivDelete('paneRestoreHash', $time);
            }
        }

        # Modify the pane's border size
        $paneObj->set_borderWidth(TRUE);
        # Add an entry for this pane object, setting the time at which its border will be restored
        # Pane object numbers are not unique to the client, so in this hash we take the unusual
        #   step of using a system time as a key, matching the time at which the border size
        #   should be restored
        $self->ivAdd('paneRestoreHash', ($self->clientTime + $self->paneDelay), $paneObj);

        return 1;
    }

    sub paneRestoreBorder {

        # Called by $self->spinClientLoop
        # In 'internal' windows, the strip object GA::Strip::Entry includes a switcher button to
        #   switch between pane objects (GA::Table::Pane); the scroll lock/split screen buttons
        #   then apply to the selected pane object
        # When a pane object is selected, $self->paneModifyBorder is called to briefly increase the
        #   pane's border size; the client loop calls this function to check whether it's time to
        #   restore any border sizes, and to restore them if so
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->paneRestoreBorder', @_);
        }

        foreach my $time ($self->ivKeys('paneRestoreHash')) {

            my $paneObj;

            if ($time < $self->clientTime) {

                $paneObj = $self->ivShow('paneRestoreHash', $time);
                # Check the pane object and its window still exist (just in case)
                if (
                    $paneObj->winObj
                    && $self->desktopObj->ivExists('gridWinHash', $paneObj->winObj->number)
                ) {
                    # Restore this pane object's border size immediately
                    $paneObj->set_borderWidth(FALSE);
                    $self->ivDelete('paneRestoreHash', $time);
                }
            }
        }

        return 1;
    }

    # Session methods

    sub startSession {

        # Called initially by GA::OtherWin::Connect->connectWorldCallback or $self->connectBlind,
        #   thereafter by GA::Cmd::Connect->do, Reconnect->do, XConnect->do, Telnet->do, SSH->do and
        #   SSL->do
        # Starts a session (managing a single connection to a world)
        #
        # Expected arguments
        #   $world          - The world's name (matches a world profile name)
        #
        # Optional arguments
        #   $host           - The world's host address (if 'undef', default host address used)
        #   $port           - The world's port (if 'undef', default host port used)
        #   $char           - A character name (matches a character profile name (if 'undef', no
        #                       character profile used)
        #   $pass           - The corresponding password (if 'undef', the world profile is consulted
        #                       to provide the password, if possible)
        #   $account        - The character's associated account name, for worlds that use both
        #                       (if 'undef', no account name used)
        #   $protocol       - If set to 'telnet', 'ssh' or 'ssl', that protocol is used; if 'undef'
        #                       or an unrecognised value, the world profile's ->protocol is used
        #   $loginMode      - Set when called by $self->connectBlind, when a new world profile is to
        #                       be created, and the user has specified what type of ->loginMode this
        #                       world uses; otherwise set to 'undef'
        #   $offlineFlag    - If TRUE, the session doesn't actually connect to the world, but still
        #                       loads all data and makes some client commands available. If FALSE
        #                       (or 'undef'), the session tries to connect to the world
        #   $tempFlag       - If set to TRUE, the world profile is a temporary world profile,
        #                       created because the user didn't specify a world name. File saving
        #                       in the session will be disabled. Otherwise set to FALSE (or
        #                       'undef')
        #
        # Return values
        #   'undef' on improper arguments or if the GA::Session object can't be created or started
        #   The new GA::Session object on success

        my (
            $self, $world, $host, $port, $char, $pass, $account, $protocol, $loginMode,
            $offlineFlag, $tempFlag, $check,
        ) = @_;

        # Local variables
        my ($actualCount, $tempName, $successFlag, $worldObj, $newSession, $index);

        # Check for improper arguments
        if (! defined $world || defined $check) {

lib/Games/Axmud/Client.pm  view on Meta::CPAN


            # Plugin not found
            return undef;
        }

        # Enable the plugin
        $pluginObj->set_enabledFlag(TRUE);

        # If any of this plugin's tasks defined text-to-speech attributes (TTS), update the
        #   our customisable attribute hashes, re-assigning existing attributes to these tasks, if
        #   necessary
        foreach my $taskName ($self->ivKeys('pluginTaskHash')) {

            if ($plugin eq $self->ivShow('pluginTaskHash', $taskName)) {

                $self->ttsAssignAttribs($taskName);
            }
        }

        # Restore the plugin's client commands. If there are any (built-in) client commands of the
        #   same name, move them somewhere else, in case this plugin is disabled again at some point
        #   in the future
        foreach my $pluginCmd ($self->ivKeys('pluginCmdHash')) {

            my ($otherPlugin, $pluginCmdObj, $originalCmdObj);

            $otherPlugin = $self->ivShow('pluginCmdHash', $pluginCmd);
            if ($otherPlugin eq $plugin) {

                if ($self->ivExists('clientCmdHash', $pluginCmd)) {

                    # Restore the plugin command, and move the original to a special IV, so that it
                    #   too can be restored if the plugin is re-disabled at some point
                    $pluginCmdObj = $self->ivShow('replaceClientCmdHash', $pluginCmd);
                    $originalCmdObj = $self->ivShow('clientCmdHash', $pluginCmd);
                    $self->ivAdd('replaceClientCmdHash', $pluginCmd, $originalCmdObj);
                    $self->ivAdd('clientCmdHash', $pluginCmd, $pluginCmdObj);

                } else {

                    # Just restore the plugin command
                    $pluginCmdObj = $self->ivShow('replaceClientCmdHash', $pluginCmd);
                    $self->ivAdd('clientCmdHash', $pluginCmd, $pluginCmdObj);
                    $self->ivDelete('replaceClientCmdHash', $pluginCmd);
                }
            }
        }

        # Also restore the plugin's client commands (along with any group headings) to
        #   $self->clientCmdPrettyList
        if ($self->ivExists('clientCmdReplacePrettyHash', $plugin)) {

            $listRef = $self->ivShow('clientCmdReplacePrettyHash', $plugin);
            if (defined $listRef && @$listRef) {

                $self->ivPush('clientCmdPrettyList', @$listRef);
            }
        }

        # Sensitise/desensitise menu bar/toolbar items, depending on current conditions
        $self->desktopObj->restrictWidgets();

        # Operation complete
        return 1;
    }

    sub disablePlugin {

        # Called by GA::Cmd::DisablePlugin->do
        # Disables a loaded plugin. Halts all of the plugin's tasks (new tasks from the plugin
        #   can't start). Closes any windows of the types added by the plugin. Disables any client
        #   commands added by the plugin
        #
        # Expected arguments
        #   $plugin     - The name of the plugin to disable
        #
        # Return values
        #   'undef' on improper arguments or if a plugin called $plugin has not been loaded
        #   1 otherwise

        my ($self, $plugin, $check) = @_;

        # Local variables
        my ($pluginObj, $listRef, $index, $matchFlag);

        # Check for improper arguments
        if (! defined $plugin || defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->disablePlugin', @_);
        }

        # Find the plugin object
        $pluginObj = $self->ivShow('pluginHash', $plugin);
        if (! $pluginObj) {

            # Plugin not found
            return undef;
        }

        # Disable the plugin
        $pluginObj->set_enabledFlag(FALSE);

        # If any of this plugin's tasks are running, in any session, halt them
        foreach my $session ($self->listSessions()) {

            foreach my $taskObj ($session->ivValues('currentTaskHash')) {

                my $thisPlugin = $self->ivShow('pluginTaskHash', $taskObj->name);

                if (defined $thisPlugin && $thisPlugin eq $plugin) {

                    # Halt the task
                    $session->pseudoCmd('halttask ' . $taskObj->uniqueName);
                }
            }
        }

        # If any of our customisable text-to-speech (TTS) hashes have assigned an attribute to
        #   this plugin's tasks, re-assign the attributes to a built-in task, if possible, or
        #   otherwise remove the attributes completely
        foreach my $taskName ($self->ivKeys('pluginTaskHash')) {

            if ($plugin eq $self->ivShow('pluginTaskHash', $taskName)) {

                $self->ttsResetAttribs($taskName);
            }
        }

        # Close any 'free' windows added by the plugin
        foreach my $winObj
            (sort {$a->number <=> $b->number} ($self->desktopObj->ivValues('freeWinHash')))
        {
            my $thisPlugin = $self->ivShow('pluginFreeWinHash', $winObj->_objClass);
            if (defined $thisPlugin && $thisPlugin eq $plugin) {

                # Close the window
                $winObj->winDestroy();
            }
        }

        # Close any 'grid' windows added by the plugin
        foreach my $winObj
            (sort {$a->number <=> $b->number} ($self->desktopObj->ivValues('freeWinHash')))
        {
            my $thisPlugin = $self->ivShow('pluginGridWinHash', $winObj->_objClass);
            if (defined $thisPlugin && $thisPlugin eq $plugin) {

                # For 'main' windows, terminate any session using the window
                if ($winObj->winType eq 'main') {

                    foreach my $session
                        (sort {$a->number <=> $b->number} ($self->ivValues('sessionHash')))
                    {
                        if ($session->mainWin && $session->mainWin eq $winObj) {

                            $self->stopSession($session);
                        }
                    }
                }

                # Close the window
                $winObj->winDestroy();
            }
        }

        # If the plugin loaded any client commands that replaced an existing client command of the
        #   same name, restore the original. Otherwise, remove the command completely
        foreach my $pluginCmd ($self->ivKeys('pluginCmdHash')) {

            my ($otherPlugin, $pluginCmdObj, $originalCmdObj);

            $otherPlugin = $self->ivShow('pluginCmdHash', $pluginCmd);
            if ($otherPlugin eq $plugin) {

                if ($self->ivExists('replaceClientCmdHash', $pluginCmd)) {

                    # Restore the original, and move the plugin command to a special IV, so that it
                    #   too can be restored if the plugin is re-enabled at some point
                    $originalCmdObj = $self->ivShow('replaceClientCmdHash', $pluginCmd);
                    $pluginCmdObj = $self->ivShow('clientCmdHash', $pluginCmd);
                    $self->ivAdd('replaceClientCmdHash', $pluginCmd, $pluginCmdObj);
                    $self->ivAdd('clientCmdHash', $pluginCmd, $originalCmdObj);

                } else {

                    # Just move the plugin command to a special IV, so that it can be restored if
                    #   the plugin is re-enabled
                    $pluginCmdObj = $self->ivShow('clientCmdHash', $pluginCmd);
                    $self->ivAdd('replaceClientCmdHash', $pluginCmd, $pluginCmdObj);
                    $self->ivDelete('clientCmdHash', $pluginCmd);
                }
            }
        }

        # Any plugin client commands should also have been added to $self->clientCmdPrettyList.
        #   Attempt to remove them
        if ($self->ivExists('clientCmdReplacePrettyHash', $plugin)) {

            $listRef = $self->ivShow('clientCmdReplacePrettyHash', $plugin);
            if (defined $listRef && @$listRef) {

                # Search through $self->clientCmdPrettyList, looking for an exact match for the
                #   contents of the list in $listRef
                $index = -1;
                do {

                    my $failFlag;

                    $index++;

                    if ($self->ivIndex('clientCmdPrettyList', $index) eq $$listRef[0]) {

                        # Found the first item in $listRef; do the rest of the items match?
                        OUTER: for (my $count = $index; $count < scalar @$listRef; $count++) {

                            if (
                                $self->ivIndex('clientCmdPrettyList', $count) ne $$listRef[$count]
                            ) {
                                # Nope! Try again on the next iteration of the do loop
                                $failFlag = TRUE;
                                last OUTER;
                            }
                        }

                        if (! $failFlag) {

                            # Found a complete match
                            $matchFlag = TRUE;
                        }
                    }

                } until ($matchFlag || $index == ((scalar $self->clientCmdPrettyList) - 1));

                if ($matchFlag) {

                    # Remove the group headings and client commands from this plugin
                    $self->ivSplice('clientCmdPrettyList', $index, scalar @$listRef);
                }
            }
        }

        # Sensitise/desensitise menu bar/toolbar items, depending on current conditions
        $self->desktopObj->restrictWidgets();

        # Operation complete
        return 1;
    }

    sub addPluginCmds {

        # Called by any Axmud plugin
        # Adds client commands defined in the plugin
        #
        # Expected arguments
        #   $plugin   - The plugin's main package (declared in the file header)
        #
        # Optional arguments
        #   @list     - A list of client commands, grouped thematically (using the same format as
        #                   $self->clientCmdPrettyList uses). If the list is empty, no commands are
        #                   added
        #
        # Return values
        #   'undef' on improper arguments or if a client command object can't be created
        #   Otherwise returns the number of client command objects created (may be 0)

        my ($self, $plugin, @list) = @_;

        # Local variables
        my (
            $pluginObj, $count,
            @clientCmdList, @clientCmdPrettyList,
            %clientCmdHash, %replaceClientCmdHash, %userCmdHash, %pluginCmdHash,
        );

        # Check for improper arguments
        if (! defined $plugin) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->addPluginCmds', @_);
        }

        # Find the plugin object
        $pluginObj = $self->ivShow('pluginHash', $plugin);
        if (! $pluginObj) {

            # Plugin not found - a very unlikely occurrence for this function, but it's worth
            #   checking anyway
            return undef;
        }

        # If no commands specified, nothing to add
        if (! @list) {

            return 0;
        }

        # Import GA::Client IVs; they are only updated if all commands are sucessfully added
        %clientCmdHash = $self->clientCmdHash;
        %replaceClientCmdHash = $self->replaceClientCmdHash;
        @clientCmdList = $self->clientCmdList;
        %userCmdHash = $self->userCmdHash;
        @clientCmdPrettyList = $self->clientCmdPrettyList;
        %pluginCmdHash = $self->pluginCmdHash;

lib/Games/Axmud/Client.pm  view on Meta::CPAN

            }
        }

        # Operation complete
        return $count;
    }

    sub addPluginMenus {

        # Called by any Axmud plugin
        # Adds menu items defined in the plugin to any menu strip object (GA::Strip::MenuBar)
        #   displayed in any 'internal' window while the client is running (and the plugin is
        #   enabled)
        #
        # Expected arguments
        #   $plugin     - The plugin's main package (declared in the file header)
        #
        # Optional arguments
        #   $funcRef    - Reference to a function which contain the code to add menu items to a
        #                   Gtk3::Menu widget, pre-existing or created by this function. The
        #                   referenced function must accept the strip object and Gtk3::Menu as
        #                   arguments, and return 'undef' on failure or 1 on success
        #
        # Return values
        #   'undef' on improper arguments or if the menu items can't be added
        #   1 otherwise

        my ($self, $plugin, $funcRef, $check) = @_;

        # Local variables
        my ($pluginObj, $subMenu);

        # Check for improper arguments
        if (! defined $plugin || ! defined $funcRef || defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->addPluginWidgets', @_);
        }

        # Find the plugin object
        $pluginObj = $self->ivShow('pluginHash', $plugin);
        if (! $pluginObj) {

            # Plugin not found - a very unlikely occurrence for this function, but it's worth
            #   checking anyway
            return undef;
        }

        # Each plugin can only call this function once
        if ($self->ivExists('pluginMenuFuncHash', $plugin)) {

            return undef;

        } else {

            $self->ivAdd('pluginMenuFuncHash', $plugin, $funcRef);
        }

        # Any 'internal' windows which already exist and which have a menu strip object should add a
        #   sub-menu for this plugin now; any new 'internal' windows created from now will
        #   automatically call the referenced function to add their own sub-menus
        foreach my $winObj ($self->desktopObj->ivValues('gridWinHash')) {

            my ($stripObj, $subMenu);

            if (
                $winObj->winType eq 'main'
                || $winObj->winType eq 'protocol'
                || $winObj->winType eq 'custom'
            ) {
                $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::MenuBar');
                if ($stripObj) {

                    $subMenu = $stripObj->addPluginWidgets($plugin);
                    if (! $subMenu) {

                        return undef;
                    }

                    # Call the referenced function to add menu items to this sub-menu
                    if (! &$funcRef($stripObj, $subMenu)) {

                        return undef;
                    }

                    # Update the window to show the new menu items
                    $winObj->winShowAll($self->_objClass . '->addPluginMenus');
                }
            }
        }

        return 1;
    }

    sub addPluginMxpFilters {

        # Called by any Axmud plugin
        # Adds the plugin function that's used to apply MXP file filters
        #
        # Expected arguments
        #   $plugin     - The plugin's main package (declared in the file header)
        #   $funcRef    - Reference to the function that does the conversion
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $plugin, $funcRef, $check) = @_;

        # Local variables
        my $pluginObj;

        # Check for improper arguments
        if (! defined $plugin || ! defined $funcRef || defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->addPluginMxpFilters', @_);
        }

        # Find the plugin object
        $pluginObj = $self->ivShow('pluginHash', $plugin);
        if (! $pluginObj) {

lib/Games/Axmud/Client.pm  view on Meta::CPAN

            $self->ivPoke('allowSoundFlag', FALSE);
        }

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_allowSoundFlag');

        # Any sessions which are connected to a world, and which are using MXP, should re-issue
        #   their <SUPPORTS> tag (this isn't part of the MXP spec, but it can't do any damage; the
        #   world will either implement an unsolicited <SUPPORTS> tag, or it won't)
        foreach my $session ($self->ivValues('sessionHash')) {

            if ($session->status eq 'connected' && $session->mxpMode eq 'client_agree') {

                # Process a fake <SUPPORT> tag, as if the world had sent one
                $session->processMxpSupportElement('<SUPPORT>', 0, 'SUPPORT');
            }
        }

        return 1;
    }

    sub set_audioCmd {

        my ($self, $cmd, $check) = @_;

        # Check for improper arguments
        if (! defined $cmd || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->set_audioCmd', @_);
        }

        $self->ivPoke('audioCmd', $cmd);

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_audioCmd');

        return 1;
    }

    sub set_autoCompleteMode {

        my ($self, $mode, $check) = @_;

        # Check for improper arguments
        if (! defined $mode || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->set_autoCompleteMode', @_);
        }

        # Update IVs
        $self->ivPoke('autoCompleteMode', $mode);
        $self->ivUndef('instructBufferPosn');
        $self->ivUndef('cmdBufferPosn');

        foreach my $session ($self->listSessions()) {

            $session->set_instructBufferPosn();
            $session->set_cmdBufferPosn();
        }

        foreach my $winObj ($self->desktopObj->ivValues('gridWinHash')) {

            $winObj->resetEntry();
        }

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_autoCompleteMode');

        return 1;
    }

    sub set_autoCompleteParent {

        my ($self, $parent, $check) = @_;

        # Check for improper arguments
        if (! defined $parent || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->set_autoCompleteParent', @_);
        }

        # Update IVs
        if ($parent eq 'client') {
            $self->ivPoke('autoCompleteParent', 'combined');
        } else {
            $self->ivPoke('autoCompleteParent', 'session');       # Initial value
        }

        $self->ivUndef('instructBufferPosn');
        $self->ivUndef('cmdBufferPosn');

        foreach my $session ($self->listSessions()) {

            $session->set_instructBufferPosn();
            $session->set_cmdBufferPosn();
        }

        foreach my $winObj ($self->desktopObj->ivValues('gridWinHash')) {

            $winObj->resetEntry();
        }

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_autoCompleteParent');

        return 1;
    }

    sub set_autoCompleteType {

        my ($self, $type, $check) = @_;

        # Check for improper arguments
        if (! defined $type || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->set_autoCompleteType', @_);
        }

        # Update IVs
        if ($type eq 'instruct') {
            $self->ivPoke('autoCompleteType', 'instruct');
        } else {
            $self->ivPoke('autoCompleteType', 'cmd');           # Initial value
        }

        $self->ivUndef('instructBufferPosn');
        $self->ivUndef('cmdBufferPosn');

        foreach my $session ($self->listSessions()) {

            $session->set_instructBufferPosn();
            $session->set_cmdBufferPosn();
        }

        foreach my $winObj ($self->desktopObj->ivValues('gridWinHash')) {

            $winObj->resetEntry();
        }

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_autoCompleteType');

        return 1;
    }

    sub set_autoConnectList {

        my ($self, @args) = @_;

        # (No improper arguments to check; @args can be an empty list)

        $self->ivPoke('autoConnectList', @args);

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_autoConnectList');

        return 1;
    }

    sub set_autoRetainFileFlag {

        my ($self, $flag, $check) = @_;

        # Check for improper arguments
        if (! defined $flag || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->set_autoRetainFileFlag', @_);
        }

        if ($flag) {
            $self->ivPoke('autoRetainFileFlag', TRUE);
        } else {
            $self->ivPoke('autoRetainFileFlag', FALSE);
        }

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_autoRetainFileFlag');

        return 1;
    }

    sub set_autoSaveFlag {

        my ($self, $flag, $check) = @_;

        # Check for improper arguments
        if (! defined $flag || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->set_autoSaveFlag', @_);
        }

        if ($flag) {
            $self->ivPoke('autoSaveFlag', TRUE);
        } else {
            $self->ivPoke('autoSaveFlag', FALSE);

lib/Games/Axmud/Client.pm  view on Meta::CPAN


        if ($type eq 'before') {
            $self->ivPoke('statusEventBeforeCount', $number);
        } else {
            $self->ivPoke('statusEventAfterCount', $number);
        }

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->set_statusEvent');

        return 1;
    }

    sub set_storeGridPosnFlag {

        my ($self, $flag, $check) = @_;

        # Check for improper arguments
        if (! defined $flag || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->set_storeGridPosnFlag', @_);
        }

        if ($flag) {
            $self->ivPoke('storeGridPosnFlag', TRUE);
        } else {
            $self->ivPoke('storeGridPosnFlag', FALSE);
        }

        return 1;
    }

    sub add_storeGridPosn {

        # Called by GA::Win::Internal->setConfigureEvent and GA::Win::Map->setConfigureEvent
        # Also called by GA::Obj::Workspace->createGridWin and ->createSimpleGridWin

        my ($self, $winObj, $xPos, $yPos, $width, $height, $ignoreFlag, $check) = @_;

        # Check for improper arguments
        if (! defined $winObj || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->add_storeGridPosn', @_);
        }

        # Do nothing if the storage flag isn't set, if it's not a 'grid' window or if it's an
        #   'external' window
        # Allow storing of position but not size, or size but not position, but not both
        if (
            (! $self->storeGridPosnFlag && ! $ignoreFlag)
            || $winObj->winCategory ne 'grid'
            || $winObj->winType eq 'external'
            || (! defined $xPos && ! defined $yPos && ! defined $width && ! defined $height)
        ) {
            return undef;
        }

        # An entry is added/replaced in $self->storeGridPosnHash for any 'grid' window, but if there
        #   are several windows with the same ->winName open, only the one which was opened first is
        #   used
        foreach my $otherWinObj ($self->desktopObj->ivValues('gridWinHash')) {

            if (
                $otherWinObj ne $winObj
                && $otherWinObj->winName eq $winObj->winName
                && $otherWinObj->number < $winObj->number
            ) {
                return undef;
            }
        }

        # Update the hash IV
        $self->ivAdd(
            'storeGridPosnHash',
            $winObj->winName,
            [$xPos, $yPos, $width, $height],
        );

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->add_storeGridPosn');

        return 1;
    }

    sub del_storeGridPosn {

        my ($self, $winName, $check) = @_;

        # Check for improper arguments
        if (! defined $winName || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->del_storeGridPosn', @_);
        }

        $self->ivDelete('storeGridPosnHash', $winName);

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->del_storeGridPosn');

        return 1;
    }

    sub reset_storeGridPosn {

        my ($self, $check) = @_;

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->reset_storeGridPosn', @_);
        }

        $self->ivEmpty('storeGridPosnHash');

        # The data stored in this IV is saved in the 'config' file
        $self->setModifyFlag('config', TRUE, $self->_objClass . '->reset_storeGridPosn');

        return 1;
    }

    sub add_systemMsg {

lib/Games/Axmud/Client.pm  view on Meta::CPAN

            return $axmud::CLIENT->writeImproper($self->_objClass . '->add_zmpPackage', @_);
        }

        # Can't replace an existing package object using one with the same name
        if ($self->ivExists('zmpPackageHash', $obj->name)) {

            return undef;

        } else {

            $self->ivAdd('zmpPackageHash', $obj->name, $obj);

            return $obj;
        }
    }

    sub add_zonemap {

        my ($self, $obj, $check) = @_;

        # Check for improper arguments
        if (! defined $obj || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->add_zonemap', @_);
        }

        # Update IVs
        $self->ivAdd('zonemapHash', $obj->name, $obj);

        # The data stored in this IV is saved in the 'zonemaps' file
        $self->setModifyFlag('zonemaps', TRUE, $self->_objClass . '->add_zonemap');

        return 1;
    }

    sub del_zonemap {

        # Called by GA::Cmd::DeleteZonemap->do and, for temporary zonemaps, GA::Session->stop
        #   (only; if a zonemap in use by a workspace grid is deleted, bad things can happen)

        my ($self, $zonemapName, $check) = @_;

        # Check for improper arguments
        if (! defined $zonemapName || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->del_zonemap', @_);
        }

        # Update IVs
        $self->ivDelete('zonemapHash', $zonemapName);

        # The data stored in this IV is saved in the 'zonemaps' file
        $self->setModifyFlag('zonemaps', TRUE, $self->_objClass . '->del_zonemap');

        return 1;
    }

    ##################
    # Accessors - get

    sub desktopObj
        { $_[0]->{desktopObj} }

    sub mainWin
        { $_[0]->{mainWin} }

    sub aboutWin
        { $_[0]->{aboutWin} }
    sub busyWin
        { $_[0]->{busyWin} }
    sub connectWin
        { $_[0]->{connectWin} }
    sub consoleWin
        { $_[0]->{consoleWin} }

    sub constIVHash
        { my $self = shift; return %{$self->{constIVHash}}; }
    sub constReservedHash
        { my $self = shift; return %{$self->{constReservedHash}}; }

    sub loadConfigFlag
        { $_[0]->{loadConfigFlag} }
    sub saveConfigFlag
        { $_[0]->{saveConfigFlag} }
    sub loadDataFlag
        { $_[0]->{loadDataFlag} }
    sub saveDataFlag
        { $_[0]->{saveDataFlag} }
    sub deleteFilesAtStartFlag
        { $_[0]->{deleteFilesAtStartFlag} }
    sub fileFailFlag
        { $_[0]->{fileFailFlag} }

    sub emergencySaveFlag
        { $_[0]->{emergencySaveFlag} }

    sub autoSaveFlag
        { $_[0]->{autoSaveFlag} }
    sub autoSaveWaitTime
        { $_[0]->{autoSaveWaitTime} }
    sub autoRetainFileFlag
        { $_[0]->{autoRetainFileFlag} }

    sub autoBackupMode
        { $_[0]->{autoBackupMode} }
    sub autoBackupDir
        { $_[0]->{autoBackupDir} }
    sub autoBackupInterval
        { $_[0]->{autoBackupInterval} }
    sub autoBackupDate
        { $_[0]->{autoBackupDate} }
    sub autoBackupFileType
        { $_[0]->{autoBackupFileType} }
    sub autoBackupAppendFlag
        { $_[0]->{autoBackupAppendFlag} }

    sub fileObjHash
        { my $self = shift; return %{$self->{fileObjHash}}; }
    sub configFileObj
        { $_[0]->{configFileObj} }
    sub configWorldProfList
        { my $self = shift; return @{$self->{configWorldProfList}}; }



( run in 0.629 second using v1.01-cache-2.11-cpan-e1769b4cff6 )