view release on metacpan or search on metacpan
lib/Games/Axmud/Session.pm view on Meta::CPAN
'Temporary worlds can\'t be saved, so any world-related data gathered during',
' this session will be lost unless you set a new world profile with the',
' \';setworld\' command.',
'Alternatively, you can close this session and start a new one. Other kinds of',
' data (initial tasks, display settings, and so on) are not affected and can',
' still be saved using the \';save\' command.',
' ',
);
foreach my $line (@list) {
$self->writeText($line);
}
}
if ($self->initOfflineFlag) {
# This session is operating in 'connect offline' mode
$self->ivPoke('status', 'offline');
# Display some reassuring text, one line at a time
@list = (
'This session is running in CONNECT OFFLINE mode - data files have been loaded as',
'usual, but ' . $axmud::SCRIPT . ' is only simulating a connection to the world',
' ',
'Current world : ' . $self->currentWorld->name,
);
if ($self->currentGuild) {
push (@list, 'Current guild : ' . $self->currentGuild->name);
}
if ($self->currentRace) {
push (@list, 'Current race : ' . $self->currentRace->name);
}
if ($self->currentChar) {
$string = $self->currentChar->name;
} else {
$string = '<none>';
}
push (@list,
'Current character : ' . $string,
' ',
);
foreach my $line (@list) {
$self->writeText($line);
}
# Update this session's tab label. The TRUE argument means definitely update it.
# (Nothing happens if the session is using a simple tab)
$self->checkTabLabels(TRUE);
# Update the connection info strip object for any 'internal' windows used by this
# session (should only be one, at this point)
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
$winObj->setHostLabel($self->getHostLabelText());
}
}
# Display a list of loaded plugins
if ($axmud::CLIENT->pluginHash) {
$pluginString = '';
foreach my $pluginObj (
sort {lc($a->name) cmp lc($b->name)} ($axmud::CLIENT->ivValues('pluginHash'))
) {
if (! $pluginObj->enabledFlag) {
$pluginString .= ' -' . $pluginObj->name;
} else {
$pluginString .= ' +' . $pluginObj->name;
}
}
$self->writeText('Plugins loaded:' . $pluginString);
$self->writeText(' ');
}
# If a world hint message is set, display it now (unless the blocking flag is set)
if (! $axmud::CLIENT->blockWorldHintFlag && $self->currentWorld->worldHint) {
if ($self->currentWorld->longName) {
$string = uc($self->currentWorld->longName);
} else {
$string = uc($self->currentWorld->name);
}
$string .= ': ' . $self->currentWorld->worldHint;
$self->writeText($string);
$self->writeText(' ');
# If this is the first connection to this world, also display the message in a
# 'dialogue' window
if (! $self->currentWorld->numberConnects) {
$self->mainWin->showMsgDialogue(
'World hint',
'info',
$string .= ' (To see this message again, type \';hint\')',
'ok',
);
}
}
# If using a charset other than the default one, display it now
if ($self->sessionCharSet ne $axmud::CLIENT->constCharSet) {
$self->writeText('Using charset \'' . $self->sessionCharSet . '\'');
$self->writeText(' ');
}
# When connecting to a world, the 'Connecting...' message will appear on this line, instead
if ($self->initOfflineFlag) {
$self->writeText('Session ready');
}
# Re-enable text-to-speech after displaying the introductory system messages
$self->ivPoke('ttsTempDisableFlag', FALSE);
# Inserting a Gtk3 update here allows all of the introductory messages actually to be
# displayed, before any text-to-speech stuff is done
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->start');
# In blind mode, and only for the first session, display some helpful information
if (
$axmud::BLIND_MODE_FLAG
&& $axmud::CLIENT->sessionCount <= 1
&& ! $axmud::CLIENT->blindHelpMsgShownFlag
) {
$self->writeText(
'Axmud is ready to start. You might like to read the help for visually-impaired'
. ' users. You can open it in your web browser. In this window, type a semicolon'
. ' followed by the word blind, without any spaces. Then press return.',
);
$self->writeText(' ');
$axmud::CLIENT->set_blindHelpMsgShownFlag(TRUE);
}
# Start the session loop (to which the maintenance, timer, incoming data, task and replay
# loops are subservient)
if (! $self->startSessionLoop()) {
return $self->writeError(
'Could not start the session loop',
$self->_objClass . '->start',
);
}
if (! $self->initOfflineFlag) {
# Attempt to connect to the world
if (! $self->doConnect($self->initHost, $self->initPort)) {
# (The return value is only false when improper arguments supplied)
return undef;
}
# If an attempted connection is immediately refused by the host, $self->status will
# already be set to 'disconnected'. In that case, we don't want to do most of the
# things usually done by the rest of this function
$self->spinIncomingLoop();
if ($self->status eq 'disconnected') {
$self->stopSessionLoop();
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
return 1;
}
}
# The session may now display received text in its 'main' window tab
$self->ivPoke('startCompleteFlag', TRUE);
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
# Handle automatic logins
if ($self->initOfflineFlag) {
# In 'connect offline' mode, the character is always marked as logged in immediately
$self->doLogin();
} else {
# Set up the automatic login (if any), but obviously don't attempt a login if we
# don't know the character's name and password
if ($self->currentWorld->loginMode ne 'none' && $self->initChar && $self->initPass) {
if (! $self->setupLogin()) {
$self->writeWarning(
'Could not set up an automatic login (in login mode '
. $self->currentWorld->loginMode
. '); use the \';login\' command after logging in manually',
$self->_objClass . '->start',
);
}
}
# Check for already-received text
if ($self->initialTextBuffer) {
# Some text has been received which we haven't displayed yet
$self->processIncomingData($self->initialTextBuffer);
# (We don't need to keep that text)
$self->ivPoke('initialTextBuffer', '');
}
}
return 1;
}
sub stop {
# Called by GA::Client->stopSession and ->stopAllSessions (only)
# Terminates the session. Any existing connection is terminated (without halting the
# session) by a call to $self->doDisconnect or to the callback $self->connectionError
#
# Expected arguments
# (none besides $self)
#
# Return values
# 'undef' on improper arguments or if the session can't be terminated
# 1 on success
my ($self, $check) = @_;
# Local variables
my $actualCount;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->stop', @_);
}
# Stop the session loop (if running)
if ($self->sessionLoopObj && ! $self->stopSessionLoop()) {
return $self->writeError(
'Could not stop the session loop',
$self->_objClass . '->stop',
);
}
# Terminate the connection, if connected (or connecting)
if (! $self->doDisconnect()) {
return $self->writeError(
'Could not terminate the connection',
$self->_objClass . '->stop',
);
}
# Update IVs
$self->ivPoke('status', 'disconnected');
# Update the world's connection history object, if one was created for this session
if ($self->connectHistoryObj) {
$self->connectHistoryObj->set_disconnectedTime();
}
# Count the number of sessions that exist, besides this one. We can't rely on
# GA::Client->sessionCount, because it might have been updated by the calling functions
$actualCount = 0;
foreach my $otherSession ($axmud::CLIENT->ivValues('sessionHash')) {
if ($otherSession ne $self) {
$actualCount++;
}
}
# Ask the 'main' window to remove the tab for this session (if allowed)
# Don't bother if sessions don't share a 'main' window (because this session's 'main'
# window is about to be closed anyway)
# (If the session has ended because the 'main' windows has been destroyed, then the call to
# $self->del_winObj will already have set $self->defaultTabObj to 'undef')
if (
$axmud::CLIENT->shareMainWinFlag
&& $self->defaultTabObj
&& ! $self->defaultTabObj->paneObj->removeSessionTab($self)
) {
return $self->writeError(
'Could not remove the tab for a session',
$self->_objClass . '->stop',
);
}
# Close any 'free' windows produced by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionFreeWins($self)) {
# As one 'free' window is closed, its child 'free' windows are also closed, so we have
# to check the window still exists, before destroying it
if ($axmud::CLIENT->desktopObj->ivExists('freeWinHash', $winObj->number)) {
$winObj->winDestroy();
}
}
# If sessions have their own workspace grids, remove the workspace grids (which closes their
# 'grid' windows, but not this session's 'main' window, which we'll deal with in a moment)
# If sessions share a workspace grid, do nothing
$axmud::CLIENT->desktopObj->removeSessionWorkspaceGrids($self);
# Remove any temporary zonemaps for this session
foreach my $zonemapObj ($axmud::CLIENT->ivValues('zonemapHash')) {
if ($zonemapObj->tempFlag && $zonemapObj->tempSession eq $self) {
$axmud::CLIENT->del_zonemap($zonemapObj);
}
}
# Check if there are any remaining 'grid' windows associated with this session and, if so,
# close them (but still don't close the 'main' window)
$axmud::CLIENT->desktopObj->removeSessionWindows($self);
# If this session has any 'external' windows on this session's workspace grid, and if this
# wasn't the current session, those 'external' windows may be invisible/minimised. Make
# them visible
$axmud::CLIENT->desktopObj->revealGridWins($self);
# Otherwise, when sessions don't share a 'main' window, we can delete it this session's
# 'main' window now
if (! $axmud::CLIENT->shareMainWinFlag) {
$self->mainWin->winDestroy();
$self->ivUndef('mainWin');
} elsif (! $actualCount && ! $axmud::CLIENT->shutdownFlag) {
# Convert the single remaining 'main' window back into a spare 'main' window
$axmud::CLIENT->desktopObj->deconvertSpareMainWin($self->mainWin);
}
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
return 1;
}
# Setup
sub setMainWin {
# Called by $self->start
# Creates a new 'main' window or re-uses an existing one
#
# Expected arguments
# $currentCount - The number of sessions that exist, besides this one (so can be 0)
#
# Return values
# 'undef' on improper arguments or if the 'main' window can't be created
# 1 on success
my ($self, $currentCount, $check) = @_;
# Local variables
my (
$winmap, $winObj, $successFlag, $thisWorkspaceObj, $thisWorkspaceGridObj, $thisZoneObj,
%workspaceHash,
);
# Check for improper arguments
if (! defined $currentCount || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->setMainWin', @_);
}
# If a winmap has been marked as the default for this world, use it (otherwise the function
# returns 'undef', and a default winmap is used)
$winmap = $self->checkWinmapWorlds($self->initWorld);
# If sessions share a 'main' window, create a workspace grid on every workspace for this
# session
# If sessions don't share a 'main' window, use the shared workspace grid on every
# workspace (or create one on every workspace, if this is the first session)
OUTER: foreach my $workspaceObj ($axmud::CLIENT->desktopObj->listWorkspaces()) {
my $gridObj;
if ($axmud::CLIENT->shareMainWinFlag) {
$gridObj = $workspaceObj->addWorkspaceGrid($self);
} elsif (! $currentCount) {
$gridObj = $workspaceObj->addWorkspaceGrid();
}
# (Temporarily storing workspace grid object makes the following call to
# ->createGridWin a lot simpler)
if ($gridObj) {
$workspaceHash{$workspaceObj->number} = $gridObj->number;
}
}
# Create a 'main' window, or use an existing one
if (
($axmud::CLIENT->shareMainWinFlag && $axmud::CLIENT->mainWin)
|| (! $axmud::CLIENT->shareMainWinFlag && ! $currentCount)
) {
# Use the existing shared 'main' window
$winObj = $axmud::CLIENT->mainWin;
}
if (
! $currentCount
&& (! $axmud::TEST_MODE_FLAG || $axmud::CLIENT->sessionCount)
&& $winObj
) {
# Convert a spare 'main' window into a normal one
if (! $axmud::CLIENT->desktopObj->convertSpareMainWin($self, $winObj, $winmap)) {
# Could not reposition the 'main' window, for some reason. Destroy it, and allow
# the code below to create a new one
$winObj->winDestroy();
$axmud::CLIENT->reset_mainWin();
$winObj = undef;
}
}
if (! $winObj) {
# Create a new 'main' window for this session, using the first available workspace. If
# >shareMainWinFlag = TRUE, we can specify the workspace grid to use, too
OUTER: foreach my $workspaceObj ($axmud::CLIENT->desktopObj->listWorkspaces()) {
$winObj = $workspaceObj->createGridWin(
'main', # Window type
'main', # Window name
undef, # Window title set automatically
$winmap, # Winmap name
'Games::Axmud::Win::Internal', # Package name
undef, # No known Gtk3::Window
undef, # No system internal ID
$self, # Owner
$self, # Owner session
$workspaceHash{$workspaceObj->number}, # 'undef' if ->shareMainWinFlag = FALSE
);
if ($winObj) {
# New 'main' window created on this workspace
last OUTER;
}
}
}
# Operation complete; if it failed, $winObj is 'undef'
return $winObj;
}
sub checkWinmapWorlds {
# Called by $self->setMainWin
# Some winmaps are marked for use as the 'default' winmap for a particular world. Ideally,
# each world should have no more than one winmap which is marked as the default for that
# world but, just in case, we'll check all winmaps alphabetically, using the first one we
# find
#
# Expected arguments
# $worldName - A world profile name (if called by $self->setMainWin, the same as
# $self->initWorld)
#
# Return values
# 'undef' on improper arguments or if this world has no default winmaps
# Otherwise, returns the first default winmap found
my ($self, $worldName, $check) = @_;
# Local variables
my @winmapList;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->checkWinmapWorlds', @_);
}
@winmapList = sort {lc($a->name) cmp lc($b->name)} ($axmud::CLIENT->ivValues('winmapHash'));
foreach my $winmapObj (@winmapList) {
if ($winmapObj->ivExists('worldHash', $worldName)) {
# This is the default winmap for the world
return $winmapObj->name;
lib/Games/Axmud/Session.pm view on Meta::CPAN
# Sound has finished playing. Should it repeat?
if ($soundObj->repeat > 1) {
# Repeat at least once more
$soundObj->ivDecrement('repeat');
$result = $axmud::CLIENT->repeatSoundFile($soundObj);
} elsif ($soundObj->repeat == -1) {
# Repeat indefinitely
$result = $axmud::CLIENT->repeatSoundFile($soundObj);
}
# If the call to ->repeatSoundFile failed, if the sound should only be played once
# or has finished repeating, delete the GA::Obj::Sound object
if (! $result) {
$soundObj->stop();
$self->ivDelete('soundHarnessHash', $soundObj->number);
}
}
}
# If auto-saves are turned on, see if it's time for an auto-save (but not during an MXP
# crosslinking operation)
if (
$axmud::CLIENT->autoSaveFlag
&& $self->status eq 'connected'
&& $self->autoSaveCheckTime
&& $self->autoSaveCheckTime < $self->sessionTime
&& $self->mxpRelocateMode eq 'none'
) {
# Perform the auto-save. In blind mode, don't read out the completion message
if ($axmud::BLIND_MODE_FLAG) {
$self->pseudoCmd('save', 'hide_complete');
} else {
$self->pseudoCmd('save');
}
$self->ivPoke('autoSaveLastTime', $self->sessionTime);
# Set the time at which the next auto-save will occur
$self->resetAutoSave();
}
# Handle changes to this session's tab label (if visible)
$self->checkTabLabels();
# Handle any Gtk3::TextView scrolling problems (see the comments in $self->new)
$self->forceScrollTextViews();
# Update any MXP gauges whose entities have been modified
$self->updateMxpGauges();
# If the GA::Strip::Entry strip object's console button is in flashing mode, check whether
# it's time to stop flashing
if ($self->systemMsgCheckTime && $self->systemMsgCheckTime < $self->sessionTime) {
$self->ivUndef('systemMsgCheckTime');
# Update strip objects for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if (
$stripObj
&& $winObj->visibleSession
&& $winObj->visibleSession eq $self
) {
$stripObj->reset_consoleIconFlash();
}
}
}
# Perform a delayed quit, if one has been set
if (defined $self->delayedQuitTime && $self->delayedQuitTime < $self->sessionTime) {
$self->clientCmd($self->delayedQuitCmd);
# (Only quit once)
$self->ivUndef('delayedQuitTime');
$self->ivUndef('delayedQuitCmd');
}
# Allow other loops to spin
$self->ivPoke('childLoopSpinFlag', FALSE);
return 1;
}
sub resetAutoSave {
# Called by $self->startMaintainLoop, ->spinMaintainLoop or GA::Cmd::AutoSave->do
# If auto-saves are turned on, sets the time (matches $self->sessionTime) at which the
# next auto-save will occur. If auto-saves are turned off, sets the IV to 0
#
# Expected arguments
# (none besides $self)
#
# Return values
# 'undef' on improper arguments
# 1 on success
my ($self, $check) = @_;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->resetAutoSave', @_);
}
if (! $axmud::CLIENT->autoSaveFlag) {
# Auto-saves turned off
$self->ivPoke('autoSaveCheckTime', 0);
$self->ivPoke('autoSaveLastTime', 0);
} else {
# Auto-saves turned on
$self->ivPoke(
'autoSaveCheckTime',
$self->sessionTime + ($axmud::CLIENT->autoSaveWaitTime * 60),
lib/Games/Axmud/Session.pm view on Meta::CPAN
}
return $labelText;
}
sub forceScrollTextViews {
# Called by $self->spinMaintainLoop and GA::Obj::Desktop->updateWidgets
# Handle any Gtk3::TextView scrolling problems. Since the update to Gtk3, Gtk3::Textviews
# sometimes fail to scroll to the top/bottom when required
# This is corrected by compiling hashes of any textviews which were told to scroll to the
# top/bottom. Periodically (once per Axmud maintain loop, and after every Gtk3 main loop
# iteration) we forcibly scroll the marked textviews to the bottom, which takes care of
# any problems (while preserving the smooth scrolling effect)
#
# Expected arguments
# (none besides $self)
#
# Optional arguments
# $gtkFlag - Set to TRUE when called by GA::Obj::Desktop->updateWidgets
#
# Return values
# 'undef' on improper arguments
# 1 on success
my ($self, $gtkFlag, $check) = @_;
# Local variables
my (%upHash, %downHash);
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->forceScrollTextViews', @_);
}
# For any textviews which had been scrolled to the top, forcibly scroll them to the top
%upHash = $self->textViewScrollUpHash;
$self->ivEmpty('textViewScrollUpHash');
foreach my $textView (values %upHash) {
$textView->get_vadjustment->set_value(0);
}
# For any textviews which had been scrolled to the bottom, forcibly scroll them to the
# bottom
%downHash = $self->textViewScrollDownHash;
$self->ivEmpty('textViewScrollDownHash');
foreach my $textView (values %downHash) {
my $adjust = $textView->get_vadjustment();
$adjust->set_value($adjust->get_upper() - $adjust->get_page_size());
}
if (%upHash || %downHash) {
# (Avoid infinite recursion)
if (! $gtkFlag) {
$axmud::CLIENT->desktopObj->updateWidgets();
}
# Check the textviews have actually scrolled to the correct position
foreach my $textView (values %upHash) {
if ($textView->get_vadjustment->get_value()) {
# Forced scrolling wasn't successful, so try again on the next maintain loop
$self->ivAdd('textViewScrollUpHash', $textView, $textView);
}
}
foreach my $textView (values %downHash) {
my $adjust = $textView->get_vadjustment();
if ($adjust->get_value() < ($adjust->get_upper() - $adjust->get_page_size())) {
# Forced scrolling wasn't successful, so try again on the next maintain loop
$self->ivAdd('textViewScrollDownHash', $textView, $textView);
}
}
}
return 1;
}
sub setCrawlMode {
# Called by GA::Cmd::Crawl->do
# Enables crawl mode, setting a temporary limit on the number of world commands that can be
# sent per second
#
# Expected arguments
# $num - The command limit per second
#
# Return values
# 'undef' on improper arguments
# 1 otherwise
my ($self, $num, $check) = @_;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->setCrawlMode', @_);
}
$self->ivPoke('crawlModeFlag', TRUE);
$self->ivPoke('crawlModeCmdLimit', $num);
$self->ivPoke('crawlModeCheckTime', $self->sessionTime + $self->crawlModeWaitTime);
return 1;
}
sub resetCrawlMode {
# Called by $self->spinMaintainLoop and GA::Cmd::Crawl->do
# Disables crawl mode, removing a temporary limit on the number of world commands that can
# be sent per second. (If the current world profile specifies a limit, that limit then
lib/Games/Axmud/Session.pm view on Meta::CPAN
# Don't call the task on the next task loop, but after a delay
$self->ivAdd(
'taskCallHash',
$taskObj->uniqueName,
$self->sessionTime + $taskObj->delayTime,
);
} else {
# Call the task on the next task loop
$self->ivAdd('taskCallHash', $taskObj->uniqueName, 0);
}
}
# Activity tasks
} elsif ($taskObj->category eq 'activity') {
if ($taskObj->shutdownFlag) {
# This an activity task whose shutdown flag has been set
$taskObj->shutdown();
} elsif ($taskObj->status eq 'reset') {
# This an activity task which must be reset
$taskObj->reset();
}
}
}
# If the data viewer window is open at the tab containing the list of current tasks,
# re-draw the list
if ($self->viewerWin) {
$currentTab = $self->viewerWin->notebookGetTab();
if (defined $currentTab && $currentTab eq 'Current tasklist') {
# If there are currently any selected lines in the tab's GA::Obj::SimpleList,
# remember them, so we can select them again as soon as the list is redrawn
@selectedList = $self->viewerWin->notebookGetSelectedLines();
# Redraw the list
$self->viewerWin->currentTaskHeader();
if (@selectedList) {
# Re-select each selected line
$self->viewerWin->notebookSetSelectedLines(@selectedList);
}
}
}
# At least one task loop has completed
$self->ivPoke('firstTaskLoopCompleteFlag', TRUE);
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions (if a task
# has started or stopped during this task loop or if a Axbasic script has resumed)
if ($resetWinFlag) {
$axmud::CLIENT->desktopObj->restrictWidgets();
}
# Allow other loops to spin
$self->ivPoke('childLoopSpinFlag', FALSE);
return 1;
}
sub compileTasks {
# Called by $self->spinTaskLoop at various points during its spin
# Extracts a list of tasks from the current tasklist
# Moves those tasks which must be processed first to the front of the list, and those tasks
# which must be processed last to the end of the list
# Returns the modified list
#
# Expected arguments
# (none besides $self)
#
# Return values
# An empty list on improper arguments
# Otherwise, returns the modified list
my ($self, $check) = @_;
# Local variables
my (@emptyList, @taskList, @initialList, @firstList, @lastList);
# Check for improper arguments
if (defined $check) {
$axmud::CLIENT->writeImproper($self->_objClass . '->compileTasks', @_);
return @emptyList;
}
# Compile a list of active process tasks that must be called. Activity tasks must also be
# called if their shutdown flag has been set, or if they must be reset
@taskList = $self->ivValues('currentTaskHash');
# Go through the list, removing any tasks on the 'first' runlist (tasks that must be run
# first, before any others)
if ($axmud::CLIENT->taskRunFirstList) {
OUTER: foreach my $string ($axmud::CLIENT->taskRunFirstList) {
@initialList = @taskList;
@taskList = ();
INNER: foreach my $taskObj (@initialList) {
if ($taskObj->name eq $string) {
push (@firstList, $taskObj);
} else {
push (@taskList, $taskObj);
}
}
}
}
# Now remove tasks on the 'last' runlist (tasks that must be run last, before any others)
lib/Games/Axmud/Session.pm view on Meta::CPAN
# Read incoming data
# v1.0.242 - Surprisingly, ->get doesn't return all the data that has been received; this
# can lead to ->processIncomingData being called to process half a line, when the rest of
# the line has actually been received by GA::Obj::Telnet (bad news for any triggers that
# might match the whole line). Therefore we need to continue polling GA::Obj::Telnet until
# it returns 'undef'
$text = '';
do {
$result = $self->connectObj->get(
Errmode => sub { }, # Do nothing on error
Timeout => 0,
);
if (defined $result) {
$text .= $result;
}
} until (! defined $result);
if ($text) {
# Check our status and amend it, if need be
if ($self->status eq 'connecting') {
$self->connectionComplete();
}
# Decode $text from the world's character set into standard Perl utf-8
if ($self->sessionCharSet ne 'null') {
$text = Encode::decode($self->sessionCharSet, $text);
}
if ($self->startCompleteFlag) {
# Display the text in the 'main' window, if $self->start has finished its jobs...
$self->processIncomingData($text);
} else {
# ...otherwise store the incoming text and wait for $self->start to finish
$self->ivPoke('initialTextBuffer', $self->initialTextBuffer . $text);
}
# If this session isn't the 'main' window's visible session, set the flag which tells
# $self->getTabLabelText (and ->checkTabLabels) that the tab's label colour should be
# changed
if ($self->mainWin->visibleSession && $self->mainWin->visibleSession ne $self) {
$self->ivPoke('showNewTextFlag', TRUE);
}
}
# Convert text to speech, if required
if ($axmud::CLIENT->systemAllowTTSFlag && $self->ttsBuffer) {
# Make sure the received text is visible in the 'main' window...
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->spinIncomingLoop');
# ...before converting text to speech
if (
$axmud::CLIENT->ttsVerboseFlag
&& defined $self->ttsLastType
&& $self->ttsLastType ne 'receive'
) {
# Last TTS conversion was something other than received text
$axmud::CLIENT->tts(
'Received text: ' . $self->ttsBuffer,
'receive',
'receive',
$self,
);
} else {
# (Don't read out 'received text' again and again and again!
$axmud::CLIENT->tts($self->ttsBuffer, 'receive', 'receive', $self);
}
}
# Always empty the buffer, in case ->systemAllowTTSFlag has been set in the last microsecond
# (or something)
$self->ivPoke('ttsBuffer', '');
# We can now display an automatic login confirmation message, if one has been prepared
if ($self->loginConfirmText) {
$self->writeText($self->loginConfirmText);
$self->ivUndef('loginConfirmText');
}
# Allow other loops to spin
$self->ivPoke('childLoopSpinFlag', FALSE);
return 1;
}
sub doConnect {
# Called by $self->start, and also by $self->mxpDoRelocate
# Attempts to connect to the world specified by $self->host and $self->port
#
# Expected arguments
# $host - The world's host address (default 127.0.0.1)
# $port - The world's port (default 23)
#
# Optional arguments
# $protocol - When called by $self->mxpDoRelocate, the protocol to use ('telnet', 'ssh'
# or 'ssl')
#
# Return values
# 'undef' on improper arguments or if the attempted connection fails
# 1 otherwise
my ($self, $host, $port, $protocol, $check) = @_;
# Local variables
my (
$user, $pass, $capProtocol, $connectObj, $longHost, $sshObj, $ptyObj, $pid, $sslObj,
$historyObj,
);
# Check for improper arguments
if (! defined $host || ! defined $port || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->doConnect', @_);
}
# Make sure any 'Connecting...' messages are visible immediately
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->doConnect');
# Decide which protocol to use, if one was not specified by the calling function
if (! $protocol) {
if ($self->initProtocol) {
$protocol = $self->initProtocol;
} elsif ($self->currentWorld->protocol) {
$protocol = $self->currentWorld->protocol;
}
}
# Failsafe - default protocol is 'telnet'. If SSL is not available on this system, any SSL
# connection reverts to a telnet connection
if (
! $protocol
|| ($protocol ne 'telnet' && $protocol ne 'ssh' && $protocol ne 'ssl')
|| ($axmud::NO_SSL_FLAG && $protocol eq 'ssl')
) {
$protocol = 'telnet';
}
# If using a temporary profile and the 'ssh' protocol, prompt the user for an SSH username/
# password. If the user declines to provide one, revert to the 'telnet' protocol
# The same thing happens if the current world profile doesn't provide at least a
# ->sshUserName
if ($protocol eq 'ssh') {
if (
($self->initTempFlag && $self->initSshFlag)
|| ! $self->currentWorld->sshUserName
) {
# Prompt the user for an SSH username/password
($user, $pass) = $self->mainWin->showDoubleEntryDialogue(
'SSH login',
'Enter the SSH username',
'Enter the SSH password',
);
if ($user && $pass) {
# Update the world profile's IVs
$self->currentWorld->ivPoke('protocol', 'ssh');
$self->currentWorld->ivPoke('sshUserName', $user);
if ($pass) {
$self->currentWorld->ivPoke('sshPassword', $pass);
}
} else {
# Default back to telnet
$protocol = 'telnet';
$self->writeText(
'SSH username/password not set; reverting to a telnet connection...',
);
$self->writeText(' '); # Blank line
}
} else {
$user = $self->currentWorld->sshUserName;
$pass = $self->currentWorld->sshPassword;
}
}
# (Make sure any system messages so far are actually visible, in case the connection hangs,
# by calling GA::Obj::Desktop->updateWidgets
if ($self->mxpRelocateMode eq 'none') {
if ($protocol eq 'telnet') {
$capProtocol = $protocol;
} else {
$capProtocol = uc($protocol);
}
$self->writeText(
'Connecting (via ' . $capProtocol . ') to \'' . $host . ' ' . $port . '\'...',
);
}
# Update some initial IVs, so that we can call $self->getHostLabelText
$self->ivPoke('protocol', $protocol);
$self->ivPoke('status', 'connecting');
# Create a new connection history object, if allowed
if ($axmud::CLIENT->connectHistoryFlag) {
$historyObj = Games::Axmud::Obj::ConnectHistory->new($self);
if ($historyObj) {
# Update session IVs
$self->ivPoke('connectHistoryObj', $historyObj);
# Update the object's ->currentTime every second
$self->ivPoke('historyCheckTIme', $self->sessionTime + 1);
# Update world profile IVs
$self->currentWorld->ivPush('connectHistoryList', $historyObj);
}
}
# Update the connection info strip object for any 'internal' windows used by this
# session (should only be one, at this point)
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
$winObj->setHostLabel($self->getHostLabelText());
}
# Make sure any system messages so far are actually visible, in case the connection hangs
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->doConnect');
# Connect to the world using the specified protocol
if ($protocol eq 'telnet') {
# Connect using GA::Obj::Telnet
$connectObj = Games::Axmud::Obj::Telnet->new(
Axmud_session => $self,
Errmode => 'return',
Timeout => $self->connectTimeOut,
);
if (! $connectObj) {
$self->writeError(
'System telnet error',
$self->_objClass . '->doConnect',
);
# React to the disconnection
$self->reactDisconnect();
# Return 'undef' to show failure
return undef;
}
} elsif ($protocol eq 'ssh') {
# The first argument in the call to Net::OpenSSH->new is in the form
# 'jack@foo.bar.com'
# 'jack:secret@foo.bar.com:10022');
# 'jsmith@2001:db8::1428:57ab'); # IPv6
# In addition, IPv6 addresses can be enclosed in brackets (which we will do)
# 'jsmith@[::1]:1022'
# Compose the first argument
$longHost = $user;
if ($pass) {
$longHost .= ':' . $pass;
}
if ($self->currentWorld->ipv6 && $self->currentWorld->ipv6 eq $host) {
$longHost .= '@[' . $host . ']';
} else {
$longHost .= '@' . $host;
}
if ($self->currentWorld->sshPortFlag) {
$longHost .= ':' . $port;
}
# Connect using Net::OpenSSH
$sshObj = Net::OpenSSH->new(
$longHost,
timeout => $self->connectTimeOut,
master_opts => [ -o => "StrictHostKeyChecking=no" ],
);
if ($sshObj) {
lib/Games/Axmud/Session.pm view on Meta::CPAN
if ($self->defaultTabObj) {
$self->defaultTabObj->textViewObj->showSystemText('Disconnected from host');
}
# Update IVs (if allowed)
if (! $flag) {
$self->ivUndef('protocol');
$self->ivUndef('connectObj');
$self->ivUndef('sshObj');
$self->ivUndef('ptyObj');
$self->ivUndef('sslObj');
if (! $axmud::CLIENT->offlineOnDisconnectFlag) {
$self->ivPoke('status', 'disconnected');
} else {
$self->ivPoke('status', 'offline');
}
$self->ivPoke('mxpRelocateMode', 'none');
$self->ivUndef('delayedQuitTime');
$self->ivUndef('delayedQuitCmd');
if (! $axmud::CLIENT->offlineOnDisconnectFlag) {
$self->ivPoke('disconnectTime', $axmud::CLIENT->localClock);
} else {
$self->ivPoke('disconnectTime', undef);
}
if (! $axmud::CLIENT->offlineOnDisconnectFlag) {
$self->ivEmpty('interfaceHash');
$self->ivEmpty('interfaceNumHash');
$self->ivPoke('interfaceCount', 0);
$self->ivEmpty('deleteInterfaceList');
$self->ivEmpty('triggerHash');
$self->ivEmpty('triggerOrderList');
$self->ivEmpty('aliasHash');
$self->ivEmpty('aliasOrderList');
$self->ivEmpty('macroHash');
$self->ivEmpty('macroOrderList');
$self->ivEmpty('timerNumHash');
$self->ivEmpty('timerClockHash');
$self->ivUndef('timerLastClock');
$self->ivEmpty('timerOrderList');
$self->ivEmpty('hookHash');
$self->ivEmpty('hookOrderList');
}
# Update the world's connection history object, if one was created for this session
if ($self->connectHistoryObj) {
$self->connectHistoryObj->set_disconnectedTime();
}
}
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
# Make sure the command entry box isn't obscured in any 'internal' windows used by this
# session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if ($stripObj) {
$stripObj->obscureEntry(FALSE);
}
}
}
# Operation complete
$self->ivPoke('doDisconnectFlag', FALSE);
return 1;
}
sub doTempDisconnect {
# Alternative to ->doDisconnect, called by $self->mxpDoRelocate
# Disconnects the current connection, but doesn't reset all IVs, in the expectation that
# some of them apply to the new server, once the connection to it is completed
#
# 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 . '->doTempDisconnect', @_);
}
# Terminate the connection
$self->connectObj->close();
# (Allow MSP sound files, if any, to continue playing)
# Show confirmation
if (! $self->mxpRelocateQuietFlag) {
$self->writeText(
'Relocating (via ' . $self->protocol . ') to new server, \'' . $self->initHost
. ' ' . $self->initPort . '\'...',
);
}
# Update (some) IVs
$self->ivUndef('connectObj');
$self->ivUndef('sshObj');
$self->ivUndef('ptyObj');
$self->ivUndef('sslObj');
$self->ivPoke('status', 'disconnected');
$self->ivPoke('loginFlag', FALSE);
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
# Make sure the command entry box isn't obscured in any 'internal' windows used by this
# session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if ($stripObj) {
$stripObj->obscureEntry(FALSE);
}
}
return 1;
}
sub reactDisconnect {
# Called by $self->connectionError when the GA::Obj::Telnet object reports an error (usually
# due to the host disconnecting us)
# Also called by ->incomingDataLoop when it reads an end-of-file (usually due to the host
# disconnecting us)
#
# Expected arguments
# (none besides $self)
#
# Optional arguments
# $flag - If TRUE, a confirmation message has already been displayed (by a call to
# $self->doDisconnect). If FALSE (or 'undef'), this function must display a
# confirmation message
#
# Return values
# 'undef' on improper arguments, if a call to $self->doDisconnect hasn't finished yet or
# if this function has already been called
# 1 otherwise
my ($self, $flag, $check) = @_;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->reactDisconnect', @_);
}
# On disconnection, this function might be called before $self->doDisconnect has finished
# (such as during blind mode, when the 'Disconnected' message is still being read aloud).
# Use a flag to prevent this
if ($self->doDisconnectFlag) {
return undef;
# On disconnection, this function is called from several places in the session code. In
# rare circumstances (such as the GA::Obj::Telnet object returning TRUE to an ->eof()
# call), it might be called more than once. Use a flag to ignore subsequent calls
} elsif ($self->reactDisconnectFlag) {
return undef;
} else {
# Ignore subsequent calls to this function
$self->ivPoke('reactDisconnectFlag', TRUE);
}
# Turn off overwrite mode in the session's default textview object (if on), allowing
lib/Games/Axmud/Session.pm view on Meta::CPAN
if ($axmud::CLIENT->offlineOnDisconnectFlag) {
$self->writeText('Switching to \'connect offline\' mode');
}
# Fire any hooks that are using the 'disconnect' hook event (but only while connected, and
# if allowed)
if ($self->status eq 'connected') {
$self->checkHooks('disconnect');
}
# Empty the repeat object and excess command lists - we don't want to continue sending
# commands after a disconnection
$self->ivEmpty('repeatObjList');
$self->ivPoke('excessCmdCount', 0);
$self->ivEmpty('excessCmdList');
$self->ivPoke('crawlModeFlag', FALSE);
$self->ivPoke('crawlModeCmdLimit', undef);
$self->ivPoke('crawlModeCheckTime', undef);
# Save files (but only while connected, or while disconnecting; and only if allowed)
if (
(
$self->status eq 'connected'
|| $self->status eq 'disconnected'
|| $self->status eq 'offline'
) && ! $self->disconnectNoSaveFlag
) {
$self->pseudoCmd('save');
}
# Update IVs
$self->ivUndef('protocol');
$self->ivUndef('connectObj');
$self->ivUndef('sshObj');
$self->ivUndef('ptyObj');
$self->ivUndef('sslObj');
if (! $axmud::CLIENT->offlineOnDisconnectFlag) {
$self->ivPoke('status', 'disconnected');
} else {
$self->ivPoke('status', 'offline');
}
$self->ivPoke('mxpRelocateMode', 'none');
$self->ivUndef('delayedQuitTime');
$self->ivUndef('delayedQuitCmd');
if (! $axmud::CLIENT->offlineOnDisconnectFlag) {
$self->ivPoke('disconnectTime', $axmud::CLIENT->localClock);
} else {
$self->ivPoke('disconnectTime', undef);
}
if (! $flag) {
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
}
if (! $axmud::CLIENT->offlineOnDisconnectFlag) {
# Remove all active interfaces
$self->ivEmpty('interfaceHash');
$self->ivEmpty('interfaceNumHash');
$self->ivPoke('interfaceCount', 0);
$self->ivEmpty('deleteInterfaceList');
$self->ivEmpty('triggerHash');
$self->ivEmpty('triggerOrderList');
$self->ivEmpty('aliasHash');
$self->ivEmpty('aliasOrderList');
$self->ivEmpty('macroHash');
$self->ivEmpty('macroOrderList');
$self->ivEmpty('timerNumHash');
$self->ivEmpty('timerClockHash');
$self->ivUndef('timerLastClock');
$self->ivEmpty('timerOrderList');
$self->ivEmpty('hookHash');
$self->ivEmpty('hookOrderList');
# Stop the session loop for this session (if running; not a fatal error if the loop
# can't be stopped, as we still need to terminate the connection itself)
if ($self->sessionLoopObj && ! $self->stopSessionLoop()) {
$self->writeError(
'Could not stop the session loop',
$self->_objClass . '->reactDisconnect',
);
}
# Close any 'free' windows produced by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionFreeWins($self)) {
# As one 'free' window is closed, its child 'free' windows are also closed, so we
# have to check the window still exists, before destroying it
if ($axmud::CLIENT->desktopObj->ivExists('freeWinHash', $winObj->number)) {
$winObj->winDestroy();
}
}
# Check if there are any remaining 'grid' windows associated with this session and, if
# so, close them (but still don't close the 'main' window)
$axmud::CLIENT->desktopObj->removeSessionWindows($self);
# If this session has any 'external' windows on this session's workspace grid, and if
# this wasn't the current session, those 'external' windows may be invisible/
# minimised. Make them visible
$axmud::CLIENT->desktopObj->revealGridWins($self);
} else {
# After switching to 'offline' mode, reset all running tasks (as if the session had just
# started in 'offline' mode
foreach my $taskObj ($self->ivValues('currentTaskHash')) {
if ($taskObj->status eq 'running' || $taskObj->status eq 'paused') {
$taskObj->set_status('reset');
}
}
# Spin the task loop so that current tasks are reset immediately
$self->spinTaskLoop();
}
# Update this session's tab label to mark the session as disconnected (or in 'offline'
# mode). The TRUE flag forces the function to update the tab label
$self->checkTabLabels(TRUE);
# Update strip objects for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj;
# Update information stored in each 'internal' window's connection info strip, if
# visible
$winObj->setHostLabel($self->getHostLabelText());
$winObj->setTimeLabel($self->getTimeLabelText());
# Reset the 'internal' window's entry box
$winObj->resetEntry();
$stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if (
$stripObj
&& $winObj->visibleSession
&& $winObj->visibleSession eq $self
) {
$stripObj->obscureEntry(FALSE);
# Must stop the console button from flashing, because this session's maintain loop
# is also halting if we're now in 'disconnected' mode
$stripObj->reset_consoleIconFlash();
}
# Reset the 'internal' window's blinkers, if any
$self->turnOffBlinker(-1); # Turn them all off
$winObj->resetBlinkers();
if (! $axmud::CLIENT->offlineOnDisconnectFlag) {
# Remove all gauges for this session, and redraw the gauge box
# The TRUE flag means that the gauge box should be removed immediately if there are
# no gauges left (belonging to other sessions), rather than waiting a few seconds,
# as we normally would
$stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::GaugeBox');
if ($stripObj) {
$stripObj->removeSessionGauges($self, TRUE);
}
}
}
# Make sure all changes are visible immediately
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->reactDisconnect');
# Update gauge IVs stored by MXP
$self->ivUndef('mxpGaugeLevel');
$self->ivEmpty('mxpGaugeHash');
return 1;
}
sub connectionError {
# Callback, called by $self->doConnect when the GA::Obj::Telnet object reports an error
# (usually due to a disconnection)
#
# Expected arguments
# $errorMsg - The error message passed by GA::Obj::Telnet
#
# Return values
# 'undef'
my ($self, $errorMsg, $check) = @_;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->connectionError', @_);
}
# NB If attempting a connection to a host, where both the host address and host port are
# invalid (c.f. 'telnet deathmud'), this function is called twice. If we are already
# disconnected, don't display a second error
if ($self->status eq 'disconnected' || $self->status eq 'offline') {
return undef;
}
# If GA::Obj::Telnet's error message is one we recognise, use our own error message
if (
$errorMsg =~ m/Name or service not known/i
|| $errorMsg =~ m/Unknown (remote|local) host/i
) {
if ($self->mxpRelocateMode eq 'none') {
$self->writeText(
'Unrecognised host \'' . $self->initHost . '\'',
$self->_objClass . '->connectionError',
);
} else {
# During an MXP crosslinking operation, show a longer message so the user isn't
# left bewildered by a sudden disconnection message when the world specified a
# <QUIET> relocation
$self->writeText(
'Relocation to new server failed, unrecognised host \''
. $self->mxpRelocateHost . '\'',
$self->_objClass . '->connectionError',
);
}
# React to the disconnection. The TRUE flag means that we've already displayed a message
lib/Games/Axmud/Session.pm view on Meta::CPAN
}
# React to the disconnection
$self->reactDisconnect(TRUE);
} else {
# Otherwise, use the error message GA::Obj::Telnet gave us
$self->writeError(
ucfirst($errorMsg),
$self->_objClass . '->connectionError',
);
# React to the disconnection. Let $self->reactDisconnect display the standard
# 'Connection terminated by host' message
$self->reactDisconnect(FALSE);
}
# GA::Obj::Telnet requires us to return 'undef'
return undef;
}
sub connectionComplete {
# Called by $self->spinIncomingLoop when the first text is received by the world, which
# signals that the connection is complete
#
# 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 . '->connectionComplete', @_);
}
# We are now connected
$self->ivPoke('status', 'connected');
if (! $self->mxpRelocateQuietFlag) {
$self->writeText('Connected');
}
# Store the time at which the connection was actually achieved, so GA::Strip::ConnectInfo
# can use it as a tooltip
# (After an MXP crosslinking operation, the time connected to the new server is displayed)
$self->ivPoke('connectedTimeString', $axmud::CLIENT->localTime());
# Update the world's connection history object, if one was created for this session
if ($self->connectHistoryObj) {
$self->connectHistoryObj->set_connectedTime();
}
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
# Update the connection info strip object for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
$winObj->setHostLabel(
$self->getHostLabelText(),
'Connected since ' . $self->connectedTimeString,
);
}
# (Make sure that message is visible immediately)
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->connectionComplete');
if ($self->currentWorld->loginMode eq 'immediate') {
# Automatic login mode 'immediate' - immediate login (character marked as 'logged in' as
# soon as the connection is established)
$self->doLogin();
} else {
# Set the time at which $self->spinMaintainLoop should show a warning that the character
# hasn't logged in yet
$self->ivPoke(
'loginWarningTime',
($self->sessionTime + $axmud::CLIENT->loginWarningTime),
);
}
# Fire any hooks that are using the 'connect' hook event
$self->checkHooks('connect');
return 1;
}
# (Process incoming data)
sub processIncomingData {
# Called by $self->spinIncomingLoop when text is received from the world
# Also called by $self->start when it's finished its setup jobs, and needs to display any
# text received from the world in the meantime
# Called by GA::Cmd::SimulateWorld->do to simulate text received from the world
#
# Processes the received text. Calls $self->tokeniseIncomingData to convert into a series
# of tokens, then processes the tokens, before calling $self->respondIncomingData to
# display a complete or partial line
#
# Expected arguments
# $text - The received text to process
#
# Optional arguments
# $noBlinkFlag - If set to TRUE, a blinker in 'internal' windows for this session is
# not turned on. If set to FALSE (or 'undef'), the blinker is turned
# on as normal. This flag can be set to TRUE if this function is
# called to display text that wasn't actually received from the world
# (e.g. when called by the ';simulateworld' client command)
#
# Return values
# 'undef' on improper arguments or if $text is an empty string
# 1 otherwise
my ($self, $text, $noBlinkFlag, $check) = @_;
# Local variables
my $enableFlag;
# Check for improper arguments
if (! defined $text || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->processIncomingData', @_);
}
lib/Games/Axmud/Session.pm view on Meta::CPAN
$self->processStripLine,
$self->processTagHash,
);
}
}
# Apply any links created by MXP <A> and <SEND> tags to the current textview (if the current
# textview was changed during the call to this function, any links for other textviews
# have already been applied)
foreach my $linkObj ($self->mxpTempLinkList) {
$self->currentTabObj->textViewObj->add_incompleteLink($linkObj);
}
$self->ivEmpty('mxpTempLinkList');
# Turn on special echo mode, if necessary
if (
$self->specialEchoMode eq 'waiting'
&& $self->defaultTabObj->textViewObj->bufferTextFlag
) {
if ($self->echoMode ne 'client_agree') {
# The world has turned off echo mode, so special echo mode is not required during
# this session
$self->ivPoke('specialEchoMode', 'normal');
} else {
# The world has not turned off echo mode, so we need to take action, depending on
# whether direct keys are enabled in this session, or not
if ($self->currentWorld->ivExists('termOverrideHash', 'useDirectKeysFlag')) {
$enableFlag = $self->currentWorld->ivShow(
'termOverrideHash',
'useDirectKeysFlag',
);
} else {
$enableFlag = $axmud::CLIENT->useDirectKeysFlag;
}
if (! $enableFlag) {
# Special echo mode is currently disabled (but might be re-enabled at any time,
# if direct keys are re-enabled in this session)
$self->ivPoke('specialEchoMode', 'disabled');
} else {
$self->ivPoke('specialEchoMode', 'enabled');
}
# Inform all strip entry objects (GA::Strip::Entry) of the change
$self->updateSpecialEcho();
}
}
# Update the connection info strip object for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
# Update information stored in each 'internal' window's connection info strip,
# if visible
$winObj->setTimeLabel($self->getTimeLabelText());
}
# Set the 'main' window's urgency hint, if allowed
if ($axmud::CLIENT->mainWinUrgencyFlag || $axmud::CLIENT->tempUrgencyFlag) {
# The TRUE argument means only set the hint, if it's not already set
$self->mainWin->setUrgent(TRUE);
# If ->tempUrgencyFlag is set (and assuming ->mainWinUrgencyFlag is not set), the 'main'
# window's urgency hint should only be set once
if ($axmud::CLIENT->tempUrgencyFlag) {
$axmud::CLIENT->set_tempUrgencyFlag(FALSE);
}
}
# Play a sound effect to signal that some text has been received, if allowed
if ($axmud::CLIENT->tempSoundFlag) {
$axmud::CLIENT->playSound('afk');
# The sound should only be played once
$axmud::CLIENT->set_tempSoundFlag(FALSE);
}
return 1;
}
sub tokeniseIncomingData {
# Called by $self->processIncomingData, and also by $self->processPuebloPaneElement
# Tokenises the text received from the world, producing a list in groups of two, in the form
# (type, argument, type, argument...)
# Where 'type' is one of the strings 'nl', 'ga', 'esc', 'inv', 'ctrl', 'seq', 'bsp', 'msp',
# 'mxp', 'ent', 'pueblo', 'mcp', 'nomcp' or 'text', and 'argument' is usually the token,
# but in the case of an escape sequence is a reference to a list of values. In that list,
# the first value is the token, the second is an argument, and the third is one of the
# sequence types 'osc', 'mxp', 'sqr', 'vt', 'xterm', 'trcol_fg' or 'trcol_bg'
#
# The list is stored in $self->currentTokenList, rather than in a local variable in the
# calling function
#
# Expected arguments
# $text - The incoming text to tokenise
#
# Optional arguments
# $insertAtStartFlag - TRUE when called by $self->processPuebloPaneElement, in which case
# the tokens are inserted at the beginning of $self->currentTokenList, so they are
# processed before any existing tokens (in other words, process the file we just
# downloaded from a URL, before continuing to process data received over the telnet/
# SSH/SSL connection). FALSE (or 'undef') otherwise
#
# Return values
# 'undef' on improper arguments or if $text is an empty string
# 1 otherwise
lib/Games/Axmud/Session.pm view on Meta::CPAN
$newTop = $resizeTop;
$newBottom = $resizeTop + $reduceHeight - 1;
$resizeTop += $reduceHeight;
} elsif ($frameObj->align eq 'bottom') {
$resizeBottom -= $reduceHeight;
$newTop = $resizeBottom + 1;
$newBottom = $newTop + $reduceHeight - 1;
}
$self->defaultTabObj->paneObj->stripObj->resizeTableObj(
$self->defaultTabObj->paneObj,
$resizeLeft,
$resizeRight,
$resizeTop,
$resizeBottom,
);
# Add a new pane object in the newly-available space
$newPaneObj = $self->defaultTabObj->paneObj->stripObj->addTableObj(
'Games::Axmud::Table::Pane',
$newLeft,
$newRight,
$newTop,
$newBottom,
'mxp_frame_' . $frameObj->name,
# Configuration hash
'frame_title' => $ivHash{'name'},
);
if (! $newPaneObj) {
$self->mxpDebug(
$origToken,
'Internal error creating frame \'' . $ivHash{'name'} . '\'',
2721,
);
return @emptyList;
}
# Add a tab
$tabObj = $newPaneObj->addSimpleTab($self);
if (! $tabObj) {
$self->mxpDebug(
$origToken,
'Internal error creating frame \'' . $ivHash{'name'} . '\'',
2722,
);
return @emptyList;
}
# This call makes the original frame's textview scroll to the bottom, as it's
# supposed to
$axmud::CLIENT->desktopObj->updateWidgets(
$self->_objClass . '->processMxpFrameElement',
);
# Update IVs
$frameObj->ivPoke('tabObj', $tabObj);
$frameObj->ivPoke('paneObj', $tabObj->paneObj);
$frameObj->ivPoke('textViewObj', $tabObj->textViewObj);
}
# Close an existing frame, if specified
} elsif ($ivHash{'action'} eq 'close') {
# Do not close the default tab's pane object, even if the world wants to
# (The MXP spec doesn't specify what to do, but Axmud will not allow it)
if ($ivHash{'name'} eq '_top') {
$self->mxpDebug(
$origToken,
'Cannot close MXP frame corresponding to the default tab',
2723,
);
return @emptyList;
}
# Close the frame
if ($frameObj->internalFlag) {
# Remove an internal frame
$self->defaultTabObj->paneObj->stripObj->removeTableObj(
$self->defaultTabObj->paneObj,
);
} else {
# Halt the Frame task
$frameObj->taskObj->set_shutdownFlag(TRUE);
}
# Update IVs
$self->ivDelete('mxpFrameHash', $frameObj->name);
if ($self->mxpCurrentFrame eq $frameObj->name) {
# If the current frame is deleted, resume using the original frame
# (The MXP spec doesn't specify what to do, so Axmud will do this)
$self->ivPoke('mxpCurrentFrame', '_top');
$self->ivPoke('currentTabObj', $self->defaultTabObj);
}
if ($self->mxpPrevFrame eq $frameObj->name) {
# Same applies to the previous frame
$self->ivPoke('mxpPrevFrame', '_top');
}
}
# Redirect text received from the world to the frame
if ($ivHash{'action'} eq 'redirect') {
$self->ivPoke('mxpPrevFrame', $self->mxpCurrentFrame);
lib/Games/Axmud/Session.pm view on Meta::CPAN
# Check that the next token to process is an MXP token
$type = $self->ivFirst('currentTokenList');
if (defined $type && $type ne 'mxp') {
$self->mxpDebug('n/a', 'Temp secure mode not followed by an MXP tag', 5004);
# Disable temp secure mode
push (@tagList, $self->setMxpLineMode($self->mxpTempMode, TRUE));
$self->ivUndef('mxpTempMode');
}
}
return @tagList;
}
sub convertMxpWinSize {
# Called by $self->processMxpFrameElement
# When the world specifies a new frame using a <FRAME> tag, it can optionally specify the
# frame's size and position
# Work out the equivalent size and position on the workspace, in pixels
#
# Expected arguments
# $frameObj - The GA::Mxp::Frame object created in response to the <FRAME> tag
#
# Return values
# An empty list on improper arguments
# Otherwise, returns a list in the form (left, top, width, height)
# ...where 'left' and 'top' are the workspace coordinates of the top-left of the proposed
# window position, and 'width' / 'height' is the size of the window, all in pixels
my ($self, $frameObj, $check) = @_;
# Local variables
my (
$workspaceObj, $availableWidth, $availableHeight, $charWidth, $charHeight, $right,
$bottom,
@emptyList, @returnList,
);
# Check for improper arguments
if (! defined $frameObj || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->convertMxpWinSize', @_);
}
# The frame is opened in the same workspace used by the session's 'main' window
# The size of the available workspace is the current width and height, minus any space
# reserved for panels
$workspaceObj = $self->mainWin->workspaceObj;
$availableWidth = $workspaceObj->currentWidth - $workspaceObj->panelLeftSize
- $workspaceObj->panelRightSize;
$availableHeight = $workspaceObj->currentHeight - $workspaceObj->panelTopSize
- $workspaceObj->panelBottomSize;
# Get size of an 'X' character (because the MXP specification demands it)
($charWidth, $charHeight) = $self->currentTabObj->textViewObj->getCharSize('X');
# $frameObj->left (etc) can be in the form 'n%' (a percentage), 'nc' (a multiple of
# character widths/heights) or 'n' (a value in pixels), relative to the left or top of the
# available desktop
# A minus value, i.e. '-n%', '-nc' or '-n' signifies that the value is relative to the
# right or bottom of the available desktop
# If an invalid value was specified, use a default value
foreach my $iv ('left', 'top', 'width', 'height') {
my ($value, $minus, $num, $type, $newValue);
$value = $frameObj->$iv;
if ($value =~ m/(\-)?(\d+)([\%c]?)/) {
$minus = $1;
$num = $2;
$type = $3;
if (! $type) {
if ($iv eq 'left' || $iv eq 'width') {
if ($minus) {
$newValue = $availableWidth - $num;
} else {
$newValue = $num;
}
} else {
if ($minus) {
$newValue = $availableHeight - $num;
} else {
$newValue = $num;
}
}
} elsif ($type eq '%') {
if ($iv eq 'left' || $iv eq 'width') {
if ($minus) {
$newValue = $availableWidth - ($availableWidth * ($num / 100));
} else {
$newValue = $availableWidth * ($num / 100);
}
} else {
if ($minus) {
$newValue = $availableHeight - ($availableHeight * ($num / 100));
} else {
$newValue = $availableHeight * ($num / 100);
}
}
} elsif ($type eq 'c') {
if ($iv eq 'left' || $iv eq 'width') {
# (Take into account spacing around the grid window's strip and table
# objects by using $axmud::CLIENT->constGridSpacingPixels; it's not
# exact, but it's good enough)
$newValue = ($charWidth * $num) + $workspaceObj->controlsLeftSize
+ $workspaceObj->controlsRightSize
+ ($axmud::CLIENT->constGridSpacingPixels * 2);
if ($minus) {
$newValue = $availableWidth - $newValue;
}
} else {
$newValue = ($charHeight * $num) + $workspaceObj->controlsTopSize
+ $workspaceObj->controlsBottomSize
+ ($axmud::CLIENT->constGridSpacingPixels * 2);
if ($minus) {
$newValue = $availableHeight - $newValue;
}
}
}
} else {
# Invalid value, so use a default value
if ($iv eq 'left' || $iv eq 'top') {
$newValue = 0;
} elsif ($iv eq 'width') {
$newValue = int($availableWidth / 2);
} else {
$newValue = int($availableHeight / 2);
}
}
push (@returnList, $newValue);
}
# Sanity checking, for the benefit of an MXP frame tag which tries to draw a window outside
# the bounds of the desktop
# Left
if ($returnList[0] < 0) {
$returnList[0] = 0;
}
# Top
if ($returnList[1] < 0) {
$returnList[1] = 0;
}
# Width
$right = $returnList[0] + $returnList[2]; # left + width
if ($right > $availableWidth) {
$returnList[2] = $availableWidth - $returnList[0]; # total width - left
}
# Height
$bottom = $returnList[1] + $returnList[3]; # top + height
if ($bottom > $availableHeight) {
$returnList[3] = $availableHeight - $returnList[1]; # total height - top
}
return @returnList;
}
sub getMxpFrame {
# Can be called by anything
# Looks up the name of an MXP frame (implemented as a Frame task window) and returns the
# corresponding frame object
# The special name '_previous' refers to $self->mxpPrevFrame, a frame in $self->mxpFrameHash
# that could have any name, so all code should call this function rather than looking up a
# frame in $self->mxpFrameHash directly
#
# Expected arguments
# $name - An MXP frame name - one of the keys in $self->mxpFrameHash, or '_previous'
#
# Return values
# 'undef' on improper arguments or if the name doesn't match an MXP frame object
# Otherwise returns the matching MXP frame object
my ($self, $name, $check) = @_;
# Check for improper arguments
if (! defined $name || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->getMxpFrame', @_);
}
if ($name eq '_previous') {
return $self->ivShow('mxpFrameHash', $self->mxpPrevFrame);
} else {
return $self->ivShow('mxpFrameHash', $name);
}
}
lib/Games/Axmud/Session.pm view on Meta::CPAN
push (@modList, $self->ivShow('mxpGaugeHash', $name));
}
}
if (@modList) {
# The FALSE argument means 'don't keep an empty gauge level'
$self->mxpGaugeStripObj->removeGauges($self, FALSE, @modList);
foreach my $obj (@modList) {
$self->ivDelete('mxpGaugeHash', $obj->number);
}
if (! $self->mxpGaugeHash) {
# All gauges have been removed
$self->mxpGaugeStripObj->removeGaugeLevel($self, $self->mxpGaugeLevel);
$self->ivUndef('mxpGaugeLevel');
}
}
}
}
return 1;
}
sub mxpDoRelocate {
# Called by $self->incomingDataLoop
# Initiaties an MXP crosslinking operation. Closes the current connection and opens a new
# one
#
# 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 . '->mxpDoRelocate', @_);
}
# If auto-saves are turned on, do an auto-save now
if ($axmud::CLIENT->autoSaveFlag && $self->autoSaveCheckTime) {
# Perform the auto-save
$self->pseudoCmd('save');
$self->ivPoke('autoSaveLastTime', $self->sessionTime);
# Set the time at which the next auto-save will occur
$self->resetAutoSave();
}
# Update the connection info strip object for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
$winObj->setHostLabel(
$self->getHostLabelText(),
'MXP crosslinking operation in progress...',
);
}
# Terminate the current connection
$self->doTempDisconnect();
$self->ivPoke('mxpRelocateMode', 'started');
# Make sure all changes are visible immediately
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->dispatchCmd');
# Intitiate the new connection
if (! $self->doConnect($self->mxpRelocateHost, $self->mxpRelocatePort, $self->protocol)) {
# Reconnection failed
$self->doDisconnect();
} else {
$self->ivPoke('mxpRelocateMode', 'wait_login');
}
return 1;
}
sub applyMxpFileFilter {
# Called by $self->processMxpImageElement and ->processMspSoundTrigger
# Given a full file path, applies the MXP file filter
# If the world has provided a plugin to convert an image/sound file its own format into a
# format supported by Axmud, call the plugin, which performs the conversion and returns
# the path to the converted file
#
# Expected arguments
# $path - Full file path to the image/sound file to convert, e.g.
# '/home/myname/axmud-data/deathmud/mxp/myimage.gff'
#
# Return values
# 'undef' on improper arguments or if the file can't be converted
# Otherwise returns the file path to the converted file (which the calling function will
# delete, after it's used), e.g. '/home/myname/axmud-data/deathmud/mxp/myimage.gif'
my ($self, $path, $check) = @_;
# Local variables
my ($file, $dir, $ext, $filterObj, $pluginObj, $funcRef);
# Check for improper arguments
if (! defined $path || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->applyMxpFileFilter', @_);
}
($file, $dir, $ext) = File::Basename::fileparse($path, qr/\.[^.]*/);
$ext =~ s/^\.//;
# Has the world specified a file filter for this file extension?
$filterObj = $self->ivShow('mxpFilterHash', $ext);
if (! defined $filterObj) {
# No file filter found, so ignore the file
return undef;
}
# Does the named plugin exist, and is it enabled?
$pluginObj = $axmud::CLIENT->ivShow('pluginHash', $filterObj->name);
if (! defined $pluginObj || ! $pluginObj->enabledFlag) {
# Plugin not available, so ignore the file
return undef;
lib/Games/Axmud/Session.pm view on Meta::CPAN
#
# Optional arguments
# @list - A list of values in the range 0-255 (can be an empty list)
#
# Return values
# An empty list on improper arguments
# Otherwise returns the modified list
my ($self, @list) = @_;
# Local variables
my @modList;
# (No improper arguments to check)
foreach my $value (@list) {
push (@modList, $value);
if ($value == 255) {
# Double escape this value
push (@modList, $value);
}
}
return @modList;
}
sub updateEcho {
# Called by $self->optCallback when the server requests that the client stop/resume ECHOing
# (also called by $self->disableTelnetOption)
# If this is the current session and the client is not ECHOing world commands, obscures text
# in the command entry box for any 'internal' windows used by this session
#
# Expected arguments
# (none besides $self)
#
# Optional arguments
# $unobscureFlag - Set to TRUE when called by $self->doLogin, in which case we unobscure
# the command entry box, regardless of the setting of $self->echoMode
#
# Return values
# 'undef' on improper arguments
# 1 otherwise
my ($self, $unobscureFlag, $check) = @_;
# Local variables
my @list;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->updateEcho', @_);
}
# Generate a list of this session's entry strip objects (the TRUE argument means 'only
# return 'internal' windows')
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if (
$stripObj
&& $stripObj->winObj->visibleSession
&& $stripObj->winObj->visibleSession eq $self
) {
push (@list, $stripObj);
}
}
foreach my $stripObj (@list) {
# (The server has suggested that the client stop ECHOing, and the client has agreed)
if ($self->echoMode eq 'client_agree' && ! $unobscureFlag) {
$stripObj->obscureEntry(TRUE);
# (The server has suggested that the client stop ECHOing, and the client has refused)
# (The server has suggested that the client resume ECHOing, and the client has agreed)
} else {
$stripObj->obscureEntry(FALSE);
}
}
# If the ECHO telnet option has been turned off and this session's special echo mode is
# enabled, disable it (extremely unlikely, but we'll check anyway)
if ($self->echoMode ne 'client_agree' && $self->specialEchoMode eq 'enabled') {
$self->ivPoke('specialEchoMode', 'disabled');
# Need to inform the strip objects. Can't incorporate this code into the foreach loop
# just above, because $unobscureFlag is a complicating factor
# (If the ECHO telnet option is turned back on at some later point, don't re-enable
# special echo mode. It's only enabled shortly after a login is completed)
foreach my $stripObj (@list) {
$stripObj->set_specialEchoFlag(FALSE);
}
}
return 1;
}
sub updateSpecialEcho {
# Called by $self->processIncomingData whenever there's a change in this session's special
# echo mode, in which case all strip entry objects (GA::Strip::Entry) must be informed
#
# 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 . '->updateSpecialEcho', @_);
}
# (The TRUE argument means 'only return 'internal' windows)
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if ($stripObj) {
# Make sure the entry box is unobscured (assuming this session is the visible one,
# and that the world has actually turned on echo mode)
# visible one)
if (
$stripObj->winObj->visibleSession
&& $stripObj->winObj->visibleSession eq $self
&& $self->echoMode eq 'client_agree'
) {
$stripObj->obscureEntry(FALSE);
}
if ($self->specialEchoMode eq 'enabled') {
# Tell the strip entry object to start sending characters to the world, one at a
# time, as soon as they're typed (but only for world commands)
$stripObj->set_specialEchoFlag(TRUE);
} else {
# Tell the strip entry object to stop doing that
$stripObj->set_specialEchoFlag(FALSE);
}
}
}
return 1;
}
sub prepareTTypeData {
# Called by $self->optCallback when the server first requests TTYPE data
# Prepares (or resets) the items to be during TTYPE option sub-negotiations
#
# Expected arguments
# (none besides $self)
#
# Return values
# 'undef' on improper arguments
# 1 otherwise
my ($self, $check) = @_;
# Local variables
my (
$termTypeMode, $customClientName, $customClientVersion, $useCtrlSeqFlag, $termType,
@termList, @itemList,
);
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->prepareTTypeData', @_);
}
# Override GA::Client IVs, if necessary
if ($self->currentWorld->ivExists('termOverrideHash', 'termTypeMode')) {
lib/Games/Axmud/Session.pm view on Meta::CPAN
}
# Get the setting that applies - the override setting in the world profile, or the
# GA::Client setting
if ($self->status eq 'offline') {
# No visible cursors in offline mode - it looks bad
$cursorFlag = FALSE;
} elsif ($self->currentWorld->ivExists('termOverrideHash', 'useVisibleCursorFlag')) {
$cursorFlag = $self->currentWorld->ivShow('termOverrideHash', 'useVisibleCursorFlag');
} else {
$cursorFlag = $axmud::CLIENT->useVisibleCursorFlag;
}
# Update the default textview. Any other parts of the code which use cursors in their
# textview objects will have to make other arrangements
if ($self->defaultTabObj) {
$self->defaultTabObj->textViewObj->set_cursorEnableFlag($cursorFlag);
}
return 1;
}
sub textViewKeysUpdate {
# Called by GA::Client->toggle_termSetting, when the value stored in
# GA::Client->useDirectKeysFlag changes
# Also called by GA::EditWin::Profile::World->saveChanges when the override settings are
# modified
#
# Responds to the changed direct keys, disabling or enabling special echo mode
#
# Expected arguments
# (none besides $self)
#
# Return values
# 'undef' on improper arguments
# 1 otherwise
my ($self, $check) = @_;
# Local variables
my (
$directFlag,
@list,
);
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->textViewKeysUpdate', @_);
}
# Generate a list of this session's entry strip objects (the TRUE argument means 'only
# return 'internal' windows')
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if (
$stripObj
&& $stripObj->winObj->visibleSession
&& $stripObj->winObj->visibleSession eq $self
) {
push (@list, $stripObj);
}
}
# Get the setting that applies - the override setting in the world profile, or the
# GA::Client setting
if ($self->currentWorld->ivExists('termOverrideHash', 'useDirectKeysFlag')) {
$directFlag = $self->currentWorld->ivShow('termOverrideHash', 'useDirectKeysFlag');
} else {
$directFlag = $axmud::CLIENT->useVisibleCursorFlag;
}
if (! $directFlag && $self->session->specialEchoMode eq 'enabled') {
$self->ivPoke('specialEchoMode', 'disabled');
foreach my $stripObj (@list) {
$stripObj->set_specialEchoFlag(FALSE);
}
} elsif ($directFlag && $self->session->specialEchoMode eq 'disabled') {
# Special echo mode can be reenabled, but only if it was disabled earlier in the
# session
$self->ivPoke('specialEchoMode', 'enabled');
foreach my $stripObj (@list) {
$stripObj->set_specialEchoFlag(TRUE);
}
}
return 1;
}
# Instructions
sub doInstruct {
# Executes an instruction (which could be a world command, a client command, a forced world
# command, an echo command, a Perl command, a script command, a multi command, a speedwalk
# command or a bypass command)
#
# Called by ->signal_connect in GA::Strip::Entry->setEntrySignals when the user types
# something in the 'main' window's command entry box and presses RETURN
# Also called by $self->perlCmd to deal with the return value of a Perl programme that's
# been executed
# Also called by $self->processLineSegment when an independent active trigger interface
# fires, which creates an instruction treated as if it had been typed by the user
# Also called by $self->checkHooks when an independent active hook interface fires, which
# creates an instruction treated as if it it had been typed by the user
# Also called by $self->checkTimers when an independent active timer interface fires, which
# creates an instruction treated as if it had been typed by the user
#
lib/Games/Axmud/Session.pm view on Meta::CPAN
} else {
return $self->writeError(
'Unrecognised client command \'' . $userCmd . '\'',
$self->_objClass . '->clientCmd',
);
}
} else {
# Get the corresponding standard (built-in) command
$standardCmd = $axmud::CLIENT->ivShow('userCmdHash', $userCmd);
}
# Check that a Perl object for this command actually exists (no reason why it shouldn't,
# but we'll check anyway)
if (! $axmud::CLIENT->ivExists('clientCmdHash', $standardCmd)) {
return $self->writeError(
'Missing client command \'' . $userCmd . '\' in registry',
$self->_objClass . '->clientCmd',
);
} else {
$cmdObj = $axmud::CLIENT->ivShow('clientCmdHash', $standardCmd);
}
# Many commands are not available after a disconnection (however, all commands are available
# in 'connect offline' mode)
if ($self->status eq 'disconnected' && ! $cmdObj->disconnectFlag) {
return $self->writeError(
'\'' . $standardCmd . '\' command unavailable while disconnected from the'
. ' world',
$self->_objClass . '->clientCmd',
);
}
# For commands whose ->noBracketFlag is TRUE, we have to re-parse $inputString, this time
# ignoring brackets (...) and diamond brackets <...>
if ($cmdObj->noBracketFlag) {
@inputWord = split(m/\s+/, $inputString);
}
# Replace the first word in @inputWord so that it's the standard (built-in) command, not the
# user command actually typed by the user
$inputWord[0] = $standardCmd;
# Call the corresponding command object's ->do function to execute the command
$result = $cmdObj->do($self, $inputString, $userCmd, @inputWord);
if (! $result) {
return undef;
} else {
# Sensitise/desensitise menu bar/toolbar items, depending on current conditions
$axmud::CLIENT->desktopObj->restrictWidgets();
# If the user typed a command like ';north portal' which was translated into
# ';allocateexit north portal', execute the corresponding world command
if ($worldCmd) {
$self->worldCmd($worldCmd);
}
return 1;
}
}
sub pseudoCmd {
# Can be called by any function which wants to execute a string as a client command, as if
# it has been typed in the 'main' window's command entry box
# e.g. The ';stopsession' command sometimes needs to call ';save'
# e.g. Axbasic scripts often need to execute strings as client commands
#
# This function allows the calling function to optionally specify whether the standard
# GA::Generic::Cmd->complete / ->error / ->improper messages should be displayed, or
# whether they should be suppressed
#
# Expected arguments
# $cmd - A string containing the pseudo command, e.g. 'setworld deathmud'. The first
# word should be a standard client command, not a user command (user commands
# will work, as long as they are still recognised, i.e. still exist in
# GA::Client->userCmdHash). The string should not begin with the client
# command sigil ';'
#
# Optional arguments
# $mode - Specifies how to display messages produced by client commands (does not affect
# how messages produced by Games::Axmud->writeText, ->writeDebug and so on are
# displayed)
# - The Axmud GUI and Automapper windows use modes 'win_error' and 'win_only'
# - Axbasic uses mode 'hide_complete'
#
# 'show_all' - show all standard messages produced by the command (with calls
# to GA::Generic::Cmd->complete, ->error and ->improper)
# 'hide_complete' - suppress messages produced by a call to
# GA::Generic::Cmd->complete (on the successful execution of a command),
# but display error messages
# 'hide_system' - suppress all standard messages produced by the command (with
# calls to GA::Generic::Cmd->complete, ->error and ->improper)
# 'win_error' - show messages produced by a call to GA::Generic::Cmd->complete
# (on the successful execution of a command) in the 'main' window, but
# show error message calls to ->error and ->improper in a 'dialogue'
# window
# 'win_only' suppress all messages produced by a call to
# GA::Generic::Cmd->complete (on the successful execution of a command),
# but show error message calls to ->error and ->improper in a 'dialogue'
# window
#
# Return values
# 'undef' on improper arguments or if an invalid $mode is specified
# Otherwise returns the result of the call to $self->clientCmd
my ($self, $cmd, $mode, $check) = @_;
lib/Games/Axmud/Session.pm view on Meta::CPAN
# If the connection is open, send the command to the world
if ($self->status eq 'connecting' || $self->status eq 'connected') {
# (Unless we're in the middle of a ';simulatecommand' operation)
if (! $self->disableWorldCmdFlag) {
# In special echo mode just send a newline character
if ($self->specialEchoMode eq 'enabled') {
$stripCmd = '';
# Telnet specifies that only US-ASCII is allowed. Filter out everything else
} elsif (
$self->sessionCharSet ne $axmud::CLIENT->constCharSet
&& $self->sessionCharSet ne 'null'
) {
# Exception - if using a non-standard character set, trust the Perl Encode
# module to take care of that stuff
$stripCmd = $encodeCmd;
} else {
$stripCmd = '';
foreach my $char (split(//, $cmd)) {
if (ord($char) >= 0 && ord($char) <= 127) {
$stripCmd .= $char;
}
}
}
# If MCP is enabled, in-band lines starting either '#$#' or '#$"' must be quoted,
# before being sent to the world
if (substr($stripCmd, 0, 3) eq '#$#' || substr($stripCmd, 0, 3) eq '#$"') {
# Quote the in-band line by preceding it with '#$"'
$stripCmd = '#$"' . $stripCmd;
}
# Send the command to the world
# Occasionally encounter an error in which this function was called to send a world
# command to a GA::Obj::Telnet object whose filehandle had just closed, so need
# to check for that
if (! $self->connectObj->eof()) {
$self->connectObj->print($stripCmd);
}
# Turn on the window blinker, and update IVs
$self->turnOnBlinker(2);
}
$self->ivIncrement('excessCmdCount');
# Fire any hooks that are using the 'send_cmd' hook event
$self->checkHooks('send_cmd', $cmd);
}
# Update the connection info strip object for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
# Update information stored in each 'internal' window's connection info strip, if
# visible
$winObj->setTimeLabel($self->getTimeLabelText());
}
# Convert text to speech, if required
if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsWorldCmdFlag) {
# Make sure the received text is visible in the 'main' window...
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->dispatchCmd');
# ...before converting text to speech
if (
$axmud::CLIENT->ttsVerboseFlag
&& defined $self->session->ttsLastType
&& $self->session->ttsLastType ne 'command'
) {
# Last TTS conversion was something other than a world command
$axmud::CLIENT->tts('Sent: ' . $cmd, 'command', 'command', $self);
} else {
# (Don't read out 'sent' again and again and again!
$axmud::CLIENT->tts($cmd, 'command', 'command', $self);
}
}
# Write to logs
$axmud::CLIENT->writeLog(
$self,
TRUE, # Not world-specific logs
$cmd,
FALSE, # Don't precede with a newline character
TRUE, # Use final newline character
'main', # Write to these files
);
return 1;
}
sub dispatchPassword {
# Called by $self->worldCmd
# Sends a command to the world which should be obscured in the current textview, because it
# contains a password. Unlike in a call to $self->dispatchCmd, the command is not recorded
# (if a recording is in progress), nor is it stored in any buffer or tested for aliases or
# hooks
# NB If $self->echoMode is set to 'client_agree', nothing is displayed in the 'main' window
# at all (as usual)
#
# Expected arguments
# $inputString - A string containing the whole world command, e.g. 'kill orc'
# $obscureString - A substring in $inputString. The substring is replaced in the 'main'
# window by asterisks, which obscures the password
#
# Return values
# 'undef' on improper arguments or if $inputString is an empty string
# 1 otherwise
my ($self, $inputString, $obscureString, $check) = @_;
# Local variables
my $bufferObj;
# Check for improper arguments
if (! defined $inputString || ! defined $obscureString || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->dispatchPassword', @_);
}
lib/Games/Axmud/Session.pm view on Meta::CPAN
}
# Obscure the command in the current textview (but if the server has suggested that the
# client stop ECHOing, and the client has agreed, don't show anything in the current
# textview)
if ($axmud::CLIENT->confirmWorldCmdFlag) {
if ($self->echoMode ne 'client_agree') {
$inputString =~ s/$obscureString/\*\*\*\*\*\*\*\*/g;
$self->currentTabObj->textViewObj->insertCmd($inputString);
} else {
# Officially, telnet clients are not supposed to insert a newline character when the
# server suggests the client stop ECHOing, and the client agrees. However, many
# worlds fail to supply their own newline character as they're supposed to. Set a
# flag that tells $self->processIncomingData to insert a newline character, if the
# packet it processes doesn't begin with one
$self->ivPoke('nlEchoFlag', TRUE);
}
} elsif ($self->promptFlag) {
# Sending a newline character cancel any prompt; even if the world command isn't
# explicitly echoed in the textview, the newline must be
$self->currentTabObj->textViewObj->insertCmd('');
}
# If $self->promptFlag is set, the most recently-received text is a command prompt
# A world command, displayed in the 'main' window's default textview, requires a newline
# character in that textview; but we only add a newline character to the received text
# (stored in the display buffer) if that received text ends in a command prompt
# (If we added a newline character all the time, a vital line in a room statement might be
# split in two, and then the Locator task won't be able to read it and the automapper will
# get lost)
# Exception - we don't insert a newline into the display buffer if echo mode is turned on;
# that's the world's responsibility
if ($self->promptFlag) {
$self->ivPoke('promptFlag', FALSE);
$self->ivPoke('promptInsertFlag', FALSE);
if ($self->displayBufferCount && $self->echoMode ne 'client_agree') {
$bufferObj = $self->ivShow('displayBufferHash', $self->displayBufferLast);
if ($bufferObj && ! $bufferObj->newLineFlag) {
$bufferObj->ivPoke('newLineFlag', TRUE);
}
}
}
# (Reset this IV in either case)
$self->ivUndef('promptCheckTime');
# Turn on the window blinker, and update IVs
$self->turnOnBlinker(2);
# Update the connection info strip object for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
# Update information stored in each 'internal' window's connection info strip, if
# visible
$winObj->setTimeLabel($self->getTimeLabelText());
}
# Convert text to speech, if required
if ($axmud::CLIENT->systemAllowTTSFlag && $axmud::CLIENT->ttsWorldCmdFlag) {
# Make sure the received text is visible in the 'main' window...
$axmud::CLIENT->desktopObj->updateWidgets($self->_objClass . '->dispatchPassword');
# ...before converting text to speech
$axmud::CLIENT->tts('Sent password', 'command', 'command', $self);
}
return 1;
}
sub checkRedirect {
# Called by $self->worldCmd to see whether a command should be processed in redirect mode.
# If so, processes the command; otherwise returns 'undef' so that the command can be
# processed as normal
# The calling function has already checked that redirect mode is on
#
# Expected arguments
# $cmd - The command to check
# $cage - The highest-priority command cage
#
# Return values
# 'undef' on improper arguments or if $cmd cannot be processed in redirect mode
# 1 if the $cmd is processed in redirect mode
my ($self, $cmd, $cage, $check) = @_;
# Local variables
my ($dirType, $flag, $redirectString, $bufferObj);
# Check for improper arguments
if (! defined $cmd || ! defined $cage || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->checkRedirect', @_);
}
# See if $cmd matches any recognised primary and/or secondary directions
$dirType = $self->currentDict->ivShow('combDirHash', $cmd);
# Redirect mode 'primary_only' - redirect primary directions
if (
$self->redirectMode eq 'primary_only'
&& defined $dirType
&& ($dirType eq 'primaryDir' || $dirType eq 'primaryAbbrev')
) {
$flag = TRUE;
# Redirect mode 'primary_secondary' - redirect primary and secondary directions (but not
# relative directions)
} elsif (
$self->redirectMode eq 'primary_secondary'
&& defined $dirType
&& $dirType ne 'relativeDir'
&& $dirType ne 'relativeAbbrev'
) {
$flag = TRUE;
# Redirect mode 'all_exits' - redirect primary and secondary directions, plus any command
# matching an exit in the current room (if set; actually the automapper's ->ghostRoom)
} elsif ($self->redirectMode eq 'all_exits') {
if (defined $dirType) {
lib/Games/Axmud/Session.pm view on Meta::CPAN
# F debug.protocol.msdp
# F debug.protocol.mxp
# F debug.protocol.mxp.comment
# F debug.protocol.pueblo
# F debug.protocol.peublo.comment
# F debug.line.numbers
# F debug.line.tags
# F debug.locator.some
# F debug.locator.all
# F debug.locator.exit
# F debug.locator.move
# F debug.obj.parse
# F debug.obj.compare
# F debug.error.plugin
# F debug.error.iv
# F debug.error.table
# F debug.error.trap
%clientHash = (
'debug.protocol.escape' => 'debugEscSequenceFlag',
'debug.protocol.telnet' => 'debugTelnetFlag',
'debug.protocol.telnet.short'
=> 'debugTelnetMiniFlag',
'debug.protocol.log' => 'debugTelnetLogFlag',
'debug.protocol.msdp' => 'debugMsdpFlag',
'debug.protocol.mxp' => 'debugMxpFlag',
'debug.protocol.mxp.comment'
=> 'debugMxpCommentFlag',
'debug.protocol.pueblo' => 'debugPuebloFlag',
'debug.protocol.pueblo.comment'
=> 'debugPuebloCommentFlag',
'debug.protocol.zmp' => 'debugZmpFlag',
'debug.protocol.atcp' => 'debugAtcpFlag',
'debug.protocol.gmcp' => 'debugGmcpFlag',
'debug.protocol.mcp' => 'debugMcpFlag',
'debug.line.numbers' => 'debugLineNumsFlag',
'debug.line.tags' => 'debugLineTagsFlag',
'debug.locator.some' => 'debugLocatorFlag',
'debug.locator.all' => 'debugMaxLocatorFlag',
'debug.locator.exit' => 'debugExitFlag',
'debug.locator.move' => 'debugMoveListFlag',
'debug.obj.parse' => 'debugParseObjFlag',
'debug.obj.compare' => 'debugCompareObjFlag',
'debug.error.plugin' => 'debugExplainPluginFlag',
'debug.error.iv' => 'debugCheckIVFlag',
'debug.error.table' => 'debugTableFitFlag',
'debug.error.trap' => 'debugTrapErrorFlag',
);
if (exists $clientHash{$string}) {
$blessed = $axmud::CLIENT;
$ivName = $clientHash{$string};
$var = $axmud::CLIENT->{$ivName};
$privFlag = TRUE;
} else {
$error = $genError;
}
} elsif ($first eq 'desktop') {
# S desktop.panel.left
# S desktop.panel.right
# S desktop.panel.top
# S desktop.panel.bottom
# S desktop.controls.left
# S desktop.controls.right
# S desktop.controls.top
# S desktop.controls.bottom
if (
$size != 3
|| ($second ne 'panel' && $second ne 'controls')
|| ($third ne 'left' && $third ne 'right' && $third ne 'top' && $third ne 'bottom')
) {
$error = $genError;
} else {
$blessed = $axmud::CLIENT;
if ($second eq 'panel') {
if ($third eq 'left') {
$ivName = 'customPanelLeftSize';
} elsif ($third eq 'right') {
$ivName = 'customPanelRightSize';
} elsif ($third eq 'top') {
$ivName = 'customPanelTopSize';
} elsif ($third eq 'bottom') {
$ivName = 'customPanelBottomSize';
}
} elsif ($second eq 'controls') {
if ($third eq 'left') {
$ivName = 'customControlsLeftSize';
} elsif ($third eq 'right') {
$ivName = 'customControlsRightSize';
} elsif ($third eq 'top') {
$ivName = 'customControlsTopSize';
} elsif ($third eq 'bottom') {
$ivName = 'customControlsBottomSize';
}
}
$var = $axmud::CLIENT->{$ivName};
$privFlag = TRUE;
}
} elsif ($first eq 'dict') {
# O dict.current
if ($second eq 'current') {
if ($size > 3) {
$error = $genError;
} elsif (! $self->currentDict) {
$error = 'No current dictionary set';
} else {
$blessed = $self->currentDict;
$privFlag = $blessed->_privFlag;
if (defined $third) {
$var = $blessed->{$last};
$ivName = $last;
lib/Games/Axmud/Session.pm view on Meta::CPAN
# S window.grid.height
# S window.text.size
# L window.charset.list
# S window.mode.tab
# F window.mode.xterm
# F window.mode.long
# F window.mode.simple
# F window.mode.toolbar
# F window.mode.irreversible
# F window.mode.urgency
# F window.mode.tooltip
# F window.confirm.close
# F window.confirm.tab
# F window.keys.scroll
# F window.keys.smooth
# F window.keys.split
# F window.keys.complete
# F window.keys.switch
%clientHash = (
'window.main.share' => 'shareMainWinFlag',
'window.main.width' => 'customMainWinWidth',
'window.main.height' => 'customMainWinHeight',
'window.grid.width' => 'customGridWinWidth',
'window.grid.height' => 'customGridWinHeight',
'window.text.size' => 'customTextBufferSize',
'window.charset.current'
=> 'charSet',
'window.charset.list' => 'charSetList',
'window.mode.tab' => 'sessionTabMode',
'window.mode.xterm' => 'xTermTitleFlag',
'window.mode.long' => 'longTabLabelFlag',
'window.mode.simple' => 'simpleTabFlag',
'window.mode.toolbar' => 'toolbarLabelFlag',
'window.mode.irreversible'
=> 'irreversibleIconFlag',
'window.mode.urgency' => 'mainWinUrgencyFlag',
'window.mode.tooltip' => 'mainWinTooltipFlag',
'window.confirm.close' => 'confirmCloseMainWinFlag',
'window.confirm.tab' => 'confirmCloseTabFlag',
'window.keys.scroll' => 'useScrollKeysFlag',
'window.keys.smooth' => 'smoothScrollKeysFlag',
'window.keys.split' => 'autoSplitKeysFlag',
'window.keys.complete' => 'useCompleteKeysFlag',
'window.keys.switch' => 'useSwitchKeysFlag',
);
if (exists $clientHash{$string}) {
$blessed = $axmud::CLIENT;
$ivName = $clientHash{$string};
$var = $axmud::CLIENT->{$ivName};
$privFlag = TRUE;
# O window.grid.NUMBER
} elsif ($second eq 'grid') {
if ($size < 3 || $size > 4) {
$error = $genError;
} elsif (! $axmud::CLIENT->desktopObj->ivExists('gridWinHash', $third)) {
if ($size == 3) {
$error = '\'Grid\' window \'' . $string . '\' doesn\'t exist';
} else {
$error = '\'Grid\' window \'' . $obj . '\' doesn\'t exist';
}
} else {
$blessed = $axmud::CLIENT->desktopObj->ivShow('gridWinHash', $third);
$privFlag = $blessed->_privFlag;
if (defined $fourth) {
$var = $blessed->{$last};
$ivName = $last;
} else {
$objFlag = TRUE;
}
}
# O window.free.NUMBER
} elsif ($second eq 'free') {
if ($size < 3 || $size > 4) {
$error = $genError;
} elsif (! $axmud::CLIENT->desktopObj->ivExists('freeWinHash', $third)) {
if ($size == 3) {
$error = '\'Free\' window \'' . $string . '\' doesn\'t exist';
} else {
$error = '\'Free\' window \'' . $obj . '\' doesn\'t exist';
}
} else {
$blessed = $axmud::CLIENT->desktopObj->ivShow('freeWinHash', $third);
$privFlag = $blessed->_privFlag;
if (defined $fourth) {
$var = $blessed->{$last};
$ivName = $last;
} else {
$objFlag = TRUE;
}
}
} else {
$error = $genError;
}
} elsif ($first eq 'winmap') {
my %clientHash;
# Save a lot of lines of code by loading IV names/values into hashes
# S winmap.default.enabled
# S winmap.default.disabled
# S winmap.default.internal
%clientHash = (
'winmap.default.enabled'
=> 'defaultEnabledWinmap',
'winmap.default.disabled'
=> 'defaultDisabledWinmap',
'winmap.default.internal'
=> 'defaultInternalWinmap',
);
if (exists $clientHash{$string}) {
$blessed = $axmud::CLIENT;
$ivName = $clientHash{$string};
$var = $axmud::CLIENT->{$ivName};
$privFlag = TRUE;
# O winmap.NAME
} else {
if ($size > 3) {
$error = $genError;
} elsif (! $axmud::CLIENT->ivExists('winmapHash', $second)) {
if ($size == 2) {
$error = 'Winmap \'' . $string . '\' doesn\'t exist';
} else {
$error = 'Winmap \'' . $obj . '\' doesn\'t exist';
}
} else {
$blessed = $axmud::CLIENT->ivShow('winmapHash', $second);
lib/Games/Axmud/Session.pm view on Meta::CPAN
'workspace.dir' => 'initWorkspaceDir',
'workspace.grid.activate'
=> 'activateGridFlag',
'workspace.grid.permit' => 'gridPermitFlag',
'workspace.grid.block' => 'gridBlockSize',
'workspace.grid.gap' => 'gridGapMaxSize',
'workspace.grid.adjust' => 'gridAdjustmentFlag',
'workspace.grid.correct'
=> 'gridEdgeCorrectionFlag',
'workspace.grid.reshuffle'
=> 'gridReshuffleFlag',
'workspace.grid.invisible'
=> 'gridInvisWinFlag',
);
if (exists $clientHash{$string}) {
$blessed = $axmud::CLIENT;
$ivName = $clientHash{$string};
$var = $axmud::CLIENT->{$ivName};
$privFlag = TRUE;
# S workspace.init.count
# S workspace.init.NUMBER
} elsif ($second eq 'init') {
if ($size != 3) {
$error = $genError;
} elsif ($third eq 'count') {
$blessed = undef;
$var = $axmud::CLIENT->ivPairs('initWorkspaceHash');
$ivName = 'initWorkspaceHash';
$privFlag = TRUE;
} elsif (! $axmud::CLIENT->ivExists('initWorkspaceHash', $third)) {
if ($size == 3) {
$error = 'Initial workspace \'' . $string . '\' doesn\'t exist';
} else {
$error = 'Initial workspace \'' . $obj . '\' doesn\'t exist';
}
} else {
$blessed = undef;
$var = $axmud::CLIENT->ivShow('initWorkspaceHash', $third);
$ivName = 'initWorkspaceHash';
$privFlag = TRUE;
}
# O workspace.obj.NUMBER
} elsif ($second eq 'obj') {
if ($size < 3 || $size > 4) {
$error = $genError;
} elsif (! $axmud::CLIENT->desktopObj->ivExists('workspaceHash', $third)) {
if ($size == 3) {
$error = 'Workspace object \'' . $string . '\' doesn\'t exist';
} else {
$error = 'Workspace object \'' . $obj . '\' doesn\'t exist';
}
} else {
$blessed = $axmud::CLIENT->desktopObj->ivShow('workspaceHash', $third);
$privFlag = $blessed->_privFlag;
if (defined $fourth) {
$var = $blessed->{$last};
$ivName = $last;
} else {
$objFlag = TRUE;
}
}
# O workspace.grid.NUMBER
} elsif ($second eq 'grid') {
if ($size < 3 || $size > 4) {
$error = $genError;
} elsif (! $axmud::CLIENT->desktopObj->ivExists('gridHash', $third)) {
if ($size == 3) {
$error = 'Workspace grid \'' . $string . '\' doesn\'t exist';
} else {
$error = 'Workspace grid \'' . $obj . '\' doesn\'t exist';
}
} else {
$blessed = $axmud::CLIENT->desktopObj->ivShow('gridHash', $third);
$privFlag = $blessed->_privFlag;
if (defined $fourth) {
$var = $blessed->{$last};
$ivName = $last;
} else {
$objFlag = TRUE;
}
}
} else {
$error = $genError;
}
} elsif ($first eq 'world') {
# o world.current
if ($second eq 'current') {
if ($size > 3) {
$error = $genError;
} elsif (! $self->currentWorld) {
$error = 'No current world profile set';
} else {
$blessed = $self->currentWorld;
$privFlag = $blessed->_privFlag;
if (defined $third) {
$var = $blessed->{$last};
$ivName = $last;
} else {
$objFlag = TRUE;
}
}
# L world.list.favourite
# L world.list.favorite
# L world.list.basic
} elsif ($second eq 'list') {
my @list;
if ($size != 3) {
$error = $genError;
} elsif ($third eq 'favourite' || $third eq 'favorite') {
$blessed = $axmud::CLIENT;
$var = $blessed->{favouriteWorldList};
lib/Games/Axmud/Session.pm view on Meta::CPAN
}
# Update the status task, if it's running
if ($self->statusTask) {
$self->statusTask->set_updateFlag(TRUE);
}
return 1;
}
sub add_systemMsg {
my ($self, $type, $msg, $check) = @_;
# Check for improper arguments
if (! defined $type || ! defined $msg || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->add_systemMsg', @_);
}
# If the Session Console window is actually open, display it there immediately
if ($self->consoleWin) {
$self->consoleWin->update($type, $msg);
} else {
$self->ivPush('systemMsgList', $type, $msg);
if ($type eq 'error' || $type eq 'warning' || $type eq 'improper') {
$self->ivPoke('systemMsgMode', 'error');
} elsif ($type eq 'debug' && $self->systemMsgMode ne 'error') {
$self->ivPoke('systemMsgMode', 'debug');
} elsif (
$type eq 'system'
&& $self->systemMsgMode ne 'error'
&& $self->systemMsgMode ne 'debug'
) {
$self->ivPoke('systemMsgMode', 'system');
}
# (The colour of the button, while flashing, might be different to the colour when it
# stops flashing)
if ($type eq 'error' || $type eq 'warning' || $type eq 'improper') {
$self->ivPoke('systemMsgTempMode', 'error');
} else {
$self->ivPoke('systemMsgTempMode', $type);
}
$self->ivPoke(
'systemMsgCheckTime',
($self->sessionTime + $self->systemMsgWaitTime),
);
# Update strip objects for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if ($stripObj) {
$stripObj->updateConsoleButton($self->systemMsgMode, $self->systemMsgTempMode);
}
}
}
return 1;
}
sub reset_systemMsg {
my ($self, $check) = @_;
# Check for improper arguments
if (defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->reset_systemMsg', @_);
}
$self->ivEmpty('systemMsgList');
$self->ivPoke('systemMsgMode', 'empty');
$self->ivPoke('systemMsgTempMode', 'empty');
$self->ivUndef('systemMsgCheckTime');
# Update strip objects for any 'internal' windows used by this session
foreach my $winObj ($axmud::CLIENT->desktopObj->listSessionGridWins($self, TRUE)) {
my $stripObj = $winObj->ivShow('firstStripHash', 'Games::Axmud::Strip::Entry');
if ($stripObj) {
$stripObj->updateConsoleButton('empty');
}
}
return 1;
}
sub add_task {
my ($self, $taskObj, $check) = @_;
# Check for improper arguments
if (! defined $taskObj || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->add_task', @_);
}
# Update IVs
$self->ivAdd('currentTaskHash', $taskObj->uniqueName, $taskObj);
$self->ivAdd('currentTaskNameHash', $taskObj->name, $taskObj);
return 1;
}
sub add_template {
my ($self, $obj, $check) = @_;
# Local variables
my @sessionList;
# Check for improper arguments
if (! defined $obj || defined $check) {
return $axmud::CLIENT->writeImproper($self->_objClass . '->add_template', @_);
}
$self->ivAdd('templateHash', $obj->category, $obj);
# If any other sessions are using the same current world profile, update their IVs, too
@sessionList = $axmud::CLIENT->findSessions($self->currentWorld->name, $self);
foreach my $session (@sessionList) {
$session->ivAdd('templateHash', $obj->category, $obj);
}
# The data stored in this IV is saved in the 'otherprof' file
$self->setModifyFlag('otherprof', TRUE, $self->_objClass . '->add_template');
return 1;
}
sub del_template {
my ($self, $obj, $check) = @_;