You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/kbackgammon/engines/fibs/kbgfibs.cpp

2315 lines
64 KiB

/* Yo Emacs, this -*- C++ -*-
Copyright (C) 1999-2001 Jens Hoefkens
jens@hoefkens.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
$Id$
*/
/*
TODO: popup dialog for accept/reject and join ??
clear the chat history?
game over, clear the caption?
need show saved
need buddy list
need wait for player,...
*/
#include "kbgfibs.h"
#include "kbgfibs.moc"
#include <kapplication.h>
#include <tdeconfig.h>
#include <tqtimer.h>
#include <tqcheckbox.h>
#include <tqlayout.h>
#include <tqstring.h>
#include <tqsocket.h>
#include <tqpopupmenu.h>
#include <tqgroupbox.h>
#include <tqbuttongroup.h>
#include <tqradiobutton.h>
#include <kmainwindow.h>
#include <klineeditdlg.h>
#include <kmessagebox.h>
#include <tqdatetime.h>
#include <tqwhatsthis.h>
#include <kaudioplayer.h>
#include <kstandarddirs.h>
#include <tqvbox.h>
#include <kiconloader.h>
#include <ktabctl.h>
#include <kpassdlg.h>
#include <tqcstring.h>
#include <knotifyclient.h>
#include <kaction.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <iostream>
#include "kbgboard.h"
#include "kbgstatus.h"
#include "clip.h"
#include "version.h"
void KBgEngineFIBS::start()
{
// FIXME: open the child windows here and not in the constructor
}
// == configuration handling ===================================================
/*
* Restore settings and ask children to do the same
*/
void KBgEngineFIBS::readConfig()
{
TDEConfig *config = kapp->config();
config->setGroup("fibs engine");
// history variables
lastAway = config->readEntry("away_hist", "");
// various options
showMsg = config->readBoolEntry("pers_msg", false);
whoisInvite = config->readBoolEntry("whois_invite", false);
// connection information
infoFIBS[FIBSHost] = config->readEntry("server", "fibs.com");
infoFIBS[FIBSPort] = config->readEntry("port", "4321");
infoFIBS[FIBSUser] = config->readEntry("user", "");
infoFIBS[FIBSPswd] = config->readEntry("password", "");
// automatic messages
useAutoMsg[MsgBeg] = config->readBoolEntry("auto-beg", false);
useAutoMsg[MsgLos] = config->readBoolEntry("auto-los", false);
useAutoMsg[MsgWin] = config->readBoolEntry("auto-win", false);
autoMsg[MsgBeg] = config->readEntry("msg-beg", "");
autoMsg[MsgLos] = config->readEntry("msg-los", "");
autoMsg[MsgWin] = config->readEntry("msg-win", "");
// ask the children to read their config options
playerlist->readConfig();
chatWindow->readConfig();
}
/*
* Save the engine specific settings and tell all clients
*/
void KBgEngineFIBS::saveConfig()
{
TDEConfig *config = kapp->config();
config->setGroup("fibs engine");
// history variables
config->writeEntry("away_hist", lastAway);
// various options
config->writeEntry("pers_msg", showMsg);
config->writeEntry("whois_invite", whoisInvite);
// connection information
config->writeEntry("server", infoFIBS[FIBSHost]);
config->writeEntry("port", infoFIBS[FIBSPort]);
config->writeEntry("user", infoFIBS[FIBSUser]);
config->writeEntry("password", infoFIBS[FIBSPswd]);
// automatic messages
config->writeEntry("auto-beg", useAutoMsg[MsgBeg]);
config->writeEntry("auto-los", useAutoMsg[MsgLos]);
config->writeEntry("auto-win", useAutoMsg[MsgWin]);
config->writeEntry("msg-beg", autoMsg[MsgBeg]);
config->writeEntry("msg-los", autoMsg[MsgLos]);
config->writeEntry("msg-win", autoMsg[MsgWin]);
// ask the children to read their config options
playerlist->saveConfig();
chatWindow->saveConfig();
}
void KBgEngineFIBS::setupDefault()
{
cbp->setChecked(false);
cbi->setChecked(false);
lec[FIBSHost]->setText("fibs.com");
lec[FIBSPort]->setText("4321");
lec[FIBSUser]->clear();
lec[FIBSPswd]->clear();
chatWindow->setupDefault();
playerlist->setupDefault();
}
void KBgEngineFIBS::setupCancel()
{
chatWindow->setupCancel();
playerlist->setupCancel();
}
/*
* Called when the setup dialog is positively closed
*/
void KBgEngineFIBS::setupOk()
{
// various options
showMsg = cbp->isChecked();
whoisInvite = cbi->isChecked();
// connection information
for (int i = 0; i < NumFIBS; i++)
infoFIBS[i] = lec[i]->text();
// automatic messages
for (int i = 0; i < NumMsg; i++) {
useAutoMsg[i] = cbm[i]->isChecked();
autoMsg[i] = lem[i]->text();
}
chatWindow->setupOk();
playerlist->setupOk();
// save settings
saveConfig();
}
/*
* Puts the FIBS specific setup into the dialog nb
*/
void KBgEngineFIBS::getSetupPages(KDialogBase *nb)
{
/*
* Main Widget
*/
TQVBox *vbp = nb->addVBoxPage(i18n("FIBS Engine"), i18n("Here you can configure the FIBS backgammon engine"),
kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop));
/*
* Get a multi page work space
*/
KTabCtl *tc = new KTabCtl(vbp, "fibs tabs");
/*
* FIBS, local options
*/
TQWidget *w = new TQWidget(tc);
TQGridLayout *gl = new TQGridLayout(w, 3, 1, nb->spacingHint());
/*
* Group boxes
*/
TQGroupBox *gbo = new TQGroupBox(i18n("Options"), w);
TQGroupBox *gbm = new TQGroupBox(i18n("Automatic Messages"), w);
gl->addWidget(gbo, 0, 0);
gl->addWidget(gbm, 1, 0);
/*
* Options
*/
cbp = new TQCheckBox(i18n("Show copy of personal messages in main window"), gbo);
cbi = new TQCheckBox(i18n("Automatically request player info on invitation"), gbo);
TQWhatsThis::add(cbp, i18n("Usually, all messages sent directly to you by other players "
"are displayed only in the chat window. Check this box if you "
"would like to get a copy of these messages in the main window."));
TQWhatsThis::add(cbi, i18n("Check this box if you would like to receive information on "
"players that invite you to games."));
cbp->setChecked(showMsg);
cbi->setChecked(whoisInvite);
gl = new TQGridLayout(gbo, 2, 1, 20);
gl->addWidget(cbp, 0, 0);
gl->addWidget(cbi, 1, 0);
/*
* Automatic messages
*/
gl = new TQGridLayout(gbm, NumMsg, 2, 20);
cbm[MsgBeg] = new TQCheckBox(i18n("Start match:"), gbm);
cbm[MsgWin] = new TQCheckBox(i18n("Win match:"), gbm);
cbm[MsgLos] = new TQCheckBox(i18n("Lose match:"), gbm);
TQWhatsThis::add(cbm[MsgBeg], i18n("If you want to send a standard greeting to your "
"opponent whenever you start a new match, check "
"this box and write the message into the entry "
"field."));
TQWhatsThis::add(cbm[MsgWin], i18n("If you want to send a standard message to your "
"opponent whenever you won a match, check this box "
"and write the message into the entry field."));
TQWhatsThis::add(cbm[MsgLos], i18n("If you want to send a standard message to your "
"opponent whenever you lost a match, check this box "
"and write the message into the entry field."));
for (int i = 0; i < NumMsg; i++) {
lem[i] = new TQLineEdit(autoMsg[i], gbm);
gl->addWidget(cbm[i], i, 0);
gl->addWidget(lem[i], i, 1);
connect(cbm[i], TQT_SIGNAL(toggled(bool)), lem[i], TQT_SLOT(setEnabled(bool)));
cbm[i]->setChecked(useAutoMsg[i]);
lem[i]->setEnabled(useAutoMsg[i]);
TQWhatsThis::add(lem[i], TQWhatsThis::textFor(cbm[i]));
}
/*
* Put the page into the notebook
*/
gl->activate();
tc->addTab(w, i18n("&Local"));
/*
* FIBS, connection setup
*/
w = new TQWidget(tc);
gl = new TQGridLayout(w, 3, 1, nb->spacingHint());
TQGroupBox *gbc = new TQGroupBox(i18n("Server"), w);
TQGroupBox *gbk = new TQGroupBox(i18n("Other"), w);
gl->addWidget(gbc, 0, 0);
gl->addWidget(gbk, 1, 0);
/*
* Server box
*/
gl = new TQGridLayout(gbc, 4, 2, 20);
TQLabel *lbc[NumFIBS];
lbc[FIBSHost] = new TQLabel(i18n("Server name:"), gbc);
lbc[FIBSPort] = new TQLabel(i18n("Server port:"), gbc);
lbc[FIBSUser] = new TQLabel(i18n("User name:"), gbc);
lbc[FIBSPswd] = new TQLabel(i18n("Password:"), gbc);
for (int i = 0; i < NumFIBS; i++) {
lec[i] = new TQLineEdit(infoFIBS[i], gbc);
gl->addWidget(lbc[i], i, 0);
gl->addWidget(lec[i], i, 1);
}
lec[FIBSPswd]->setEchoMode(TQLineEdit::Password);
TQWhatsThis::add(lec[FIBSHost], i18n("Enter here the host name of FIBS. With almost "
"absolute certainty this should be \"fibs.com\". "
"If you leave this blank, you will be asked again "
"at connection time."));
TQWhatsThis::add(lec[FIBSPort], i18n("Enter here the port number of FIBS. With almost "
"absolute certainty this should be \"4321\". "
"If you leave this blank, you will be asked again "
"at connection time."));
TQWhatsThis::add(lec[FIBSUser], i18n("Enter your login on FIBS here. If you do not have a "
"login yet, you should first create an account using "
"the corresponding menu entry. If you leave this blank, "
"you will be asked again at connection time."));
TQWhatsThis::add(lec[FIBSPswd], i18n("Enter your password on FIBS here. If you do not have a "
"login yet, you should first create an account using "
"the corresponding menu entry. If you leave this blank, "
"you will be asked again at connection time. The password "
"will not be visible."));
/*
* Connection keepalive
*/
cbk = new TQCheckBox(i18n("Keep connections alive"), gbk);
TQWhatsThis::add(cbk, i18n("Usually, FIBS drops the connection after one hour of inactivity. When "
"you check this box, %1 will try to keep the connection alive, even "
"if you are not actually playing or chatting. Use this with caution "
"if you do not have flat-rate Internet access.").arg(PROG_NAME));
cbk->setChecked(keepalive);
gl = new TQGridLayout(gbk, 1, 1, nb->spacingHint());
gl->addWidget(cbk, 0, 0);
/*
* Done with the page, put it in
*/
gl->activate();
tc->addTab(w, i18n("&Connection"));
/*
* Ask children for settings
*/
chatWindow->getSetupPages(tc, nb->spacingHint());
playerlist->getSetupPages(tc, nb->spacingHint());
/*
* TODO: future extensions
*/
w = new TQWidget(tc);
tc->addTab(w, i18n("&Buddy List"));
}
// == functions related to the invitation menu =================================
/*
* Remove a player from the invitation list in the join menu
*/
void KBgEngineFIBS::cancelJoin(const TQString &info)
{
TQRegExp patt = TQRegExp("^" + info + " ");
for (int i = 0; i <= numJoin; i++) {
if (actJoin[i]->text().contains(patt)) {
// move all entries starting at i+1 up by one...
for (int j = i; j < numJoin; j++)
actJoin[j]->setText(actJoin[j+1]->text());
actJoin[numJoin--]->unplug(joinMenu);
break;
}
}
}
/*
* Parse the information in info for the purposes of the invitation
* submenu
*/
void KBgEngineFIBS::changeJoin(const TQString &info)
{
char name_p[100], name_o[100];
float rate;
int expi;
/*
* Extract the name of the player, her opponent, rating and experience.
* It is okay to use latin1(), since the string is coming from FIBS.
*/
sscanf(info.latin1(), "%99s %99s %*s %*s %*s %f %i %*s %*s %*s %*s %*s",
name_p, name_o, &rate, &expi);
TQString name = name_p;
TQString oppo = name_o;
TQString rate_s; rate_s.setNum(rate);
TQString expi_s; expi_s.setNum(expi);
TQRegExp patt = TQRegExp("^" + name + " ");
/*
* We have essentially two lists of names to check against: the ones
* that have invited us and are not yet in the menu and the ones that
* are already in the menu.
*/
if (numJoin > -1 && oppo != "-")
cancelJoin(name);
for (TQStringList::Iterator it = invitations.begin(); it != invitations.end(); ++it) {
if ((*it).contains(patt)) {
TQString text, menu;
if ((*it).contains(TQRegExp(" r$"))) {
menu = i18n("R means resume", "%1 (R)").arg(name);
text = i18n("%1 (experience %2, rating %3) wants to resume a saved match with you. "
"If you want to play, use the corresponding menu entry to join (or type "
"'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name);
KNotifyClient::event("invitation", i18n("%1 wants to resume a saved match with you").
arg(name));
} else if ((*it).contains(TQRegExp(" u$"))) {
menu = i18n("U means unlimited", "%1 (U)").arg(name);
text = i18n("%1 (experience %2, rating %3) wants to play an unlimited match with you. "
"If you want to play, use the corresponding menu entry to join (or type "
"'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name);
KNotifyClient::event("invitation", i18n("%1 has invited you to an unlimited match").
arg(name));
} else {
TQString len = (*it).right((*it).length() - name.length() - 1);
menu = i18n("If the format of the (U) and (R) strings is changed, it should also be changed here",
"%1 (%2)").arg(name).arg(len);
text = i18n("%1 (experience %2, rating %3) wants to play a %4 point match with you. "
"If you want to play, use the corresponding menu entry to join (or type "
"'join %5').").arg(name).arg(expi_s).arg(rate_s).arg(len).arg(name);
KNotifyClient::event("invitation", i18n("%1 has invited you for a %2 point match").
arg(name).arg(len));
}
emit serverString("rawwho " + name); // this avoids a race
if (whoisInvite) {
emit serverString("whois " + name);
emit infoText("<font color=\"red\">" + text + "</font>");
} else
emit infoText("<font color=\"red\">" + text + "</font><br>");
for (int i = 0; i <=numJoin; i++)
actJoin[i]->unplug(joinMenu);
if (++numJoin > 7) numJoin = 7;
for (int i = numJoin; i > 0; i--)
actJoin[i]->setText(actJoin[i-1]->text());
actJoin[0]->setText(menu);
for (int i = 0; i <= numJoin; i++)
actJoin[i]->plug(joinMenu);
invitations.remove(it);
break;
}
}
/*
* If there are entries in the menu, enable it
*/
menu->setItemEnabled(joinMenuID, numJoin > -1);
}
// == various slots and functions ==============================================
/*
* Keep the connection alive.
*/
void KBgEngineFIBS::keepAlive()
{
emit serverString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
/*
* Several bookkeeping operations that have to be done at the
* end of every game. Some of these may or may not be necessary
* at a particular time, but they don't hurt either.
*/
void KBgEngineFIBS::endGame()
{
playing = false;
emit serverString("rawwho " + infoFIBS[FIBSUser]);
actConti->setEnabled(false);
actLeave->setEnabled(false);
actAccept->setEnabled(false);
actReject->setEnabled(false);
emit allowCommand(Load, false);
emit allowCommand(Undo, false);
emit allowCommand(Done, false);
emit allowCommand(Cube, false);
emit allowCommand(Roll, false);
}
/*
* Toggle visibility of the player list
*/
void KBgEngineFIBS::showList()
{
playerlist->isVisible() ? playerlist->hide() : playerlist->show();
}
/*
* Toggle visibility of the chat window
*/
void KBgEngineFIBS::showChat()
{
chatWindow->isVisible() ? chatWindow->hide() : chatWindow->show();
}
/*
* Process the last move coming from the board
*/
void KBgEngineFIBS::handleMove(TQString *s)
{
lastMove = *s;
TQString t = lastMove.left(1);
int moves = t.toInt();
emit allowCommand(Done, moves == toMove);
emit allowCommand(Undo, moves > 0);
/*
* Allow undo and possibly start the commit timer
*/
redoPossible &= ((moves < toMove) && (undoCounter > 0));
emit allowCommand(Redo, redoPossible);
if (moves == toMove && cl >= 0) {
emit allowMoving(false);
ct->start(cl, true);
}
}
/*
* Done with the move
*/
void KBgEngineFIBS::done()
{
// prevent the timer from expiring again
ct->stop();
// no more moves
emit allowMoving(false);
// no more commands until it's our turn
emit allowCommand(Load, false);
emit allowCommand(Undo, false);
emit allowCommand(Done, false);
emit allowCommand(Cube, false);
emit allowCommand(Roll, false);
// Transform the string to FIBS cormat
lastMove.replace(0, 2, "move ");
lastMove.replace(pat[PlsChar], "-");
// sent it to the server
emit serverString(lastMove);
}
/*
* Undo the last move
*/
void KBgEngineFIBS::undo()
{
ct->stop();
redoPossible = true;
++undoCounter;
emit allowMoving(true);
emit allowCommand(Done, false);
emit allowCommand(Redo, true);
emit undoMove();
}
/*
* Redo the last undone move
*/
void KBgEngineFIBS::redo()
{
--undoCounter;
emit redoMove();
}
/*
* Double the cube - coming from the board
*/
void KBgEngineFIBS::doubleCube(const int w)
{
if (playing && w == US) cube();
}
/*
* Roll the dice - coming from the board
*/
void KBgEngineFIBS::rollDice(const int w)
{
if (playing && w == US) roll();
}
/*
* This engine passes all commands unmodified to the server
*/
void KBgEngineFIBS::handleCommand(TQString const &cmd)
{
emit serverString(cmd);
}
/*
* If we have a connection, we don't quit right away
*/
bool KBgEngineFIBS::queryClose()
{
if (connection->state() == TQSocket::Idle)
return true;
switch (KMessageBox::warningYesNoCancel((TQWidget *)parent(),i18n("Still connected. Log out first?"),TQString(),i18n("Log Out"), i18n("Stay Connected"))) {
case KMessageBox::Yes :
disconnectFIBS();
return true;
case KMessageBox::No :
return true;
default: // cancel
return false;
}
}
/*
* If we have a connection, we don't quit right away
*/
bool KBgEngineFIBS::queryExit()
{
if( kapp->sessionSaving())
return true;
if (connection->state() != TQSocket::Idle)
disconnectFIBS();
return true;
}
/*
* This displays a copy of personal messages in the main window.
* Normally, these only get displayed in the chat window.
*/
void KBgEngineFIBS::personalMessage(const TQString &msg)
{
if (showMsg)
emit infoText(msg);
}
// == slots and functions for FIBS commands ====================================
/*
* Accept the offer
*/
void KBgEngineFIBS::accept()
{
actAccept->setEnabled(false);
actReject->setEnabled(false);
emit serverString("accept");
}
/*
* Reject the offer
*/
void KBgEngineFIBS::reject()
{
actAccept->setEnabled(false);
actReject->setEnabled(false);
emit serverString("reject");
}
/*
* Continue a multi game match
*/
void KBgEngineFIBS::match_conti()
{
actConti->setEnabled(false);
actLeave->setEnabled(false);
emit serverString("join");
}
/*
* Leave a multi game match
*/
void KBgEngineFIBS::match_leave()
{
actConti->setEnabled(false);
actLeave->setEnabled(false);
emit serverString("leave");
}
/*
* Go away from the server for a little while. Offer the last know away
* message as a default to the user.
*/
void KBgEngineFIBS::away()
{
bool ret;
TQString msg = KLineEditDlg::getText(i18n("Please type the message that should be displayed to other\n"
"users while you are away."),
lastAway, &ret, (TQWidget *)parent());
if (ret) {
lastAway = msg;
emit serverString("away " + msg);
actAway->setEnabled(false);
}
}
/*
* Toggle being ready for games
*/
void KBgEngineFIBS::toggle_ready()
{
emit serverString("toggle ready");
}
/*
* Toggle the use of greedy bearoffs
*/
void KBgEngineFIBS::toggle_greedy()
{
emit serverString("toggle greedy");
}
/*
* Toggle whether we will be asked to double/roll or not
*/
void KBgEngineFIBS::toggle_double()
{
emit serverString("toggle double");
}
/*
* Toggle whether we want to see details on rating computations
*/
void KBgEngineFIBS::toggle_ratings()
{
emit serverString("toggle ratings");
}
/*
* Come back after being away.
*/
void KBgEngineFIBS::back()
{
emit serverString("back");
}
/*
* Double the cube
*/
void KBgEngineFIBS::cube()
{
emit serverString("double");
}
/*
* Roll the dice
*/
void KBgEngineFIBS::roll()
{
emit serverString("roll");
}
/*
* Reload the board
*/
void KBgEngineFIBS::load()
{
emit serverString("board");
}
/*
* Handle the menu short cuts for joining. This is not as pretty as it
* could or should be, but it works and is easy to understand.
*/
void KBgEngineFIBS::join(const TQString &msg)
{
emit serverString("join " + msg.left(msg.find('(')));
}
void KBgEngineFIBS::join_0() { join(actJoin[0]->text()); }
void KBgEngineFIBS::join_1() { join(actJoin[1]->text()); }
void KBgEngineFIBS::join_2() { join(actJoin[2]->text()); }
void KBgEngineFIBS::join_3() { join(actJoin[3]->text()); }
void KBgEngineFIBS::join_4() { join(actJoin[4]->text()); }
void KBgEngineFIBS::join_5() { join(actJoin[5]->text()); }
void KBgEngineFIBS::join_6() { join(actJoin[6]->text()); }
void KBgEngineFIBS::join_7() { join(actJoin[7]->text()); }
// == invitation handling ======================================================
/*
* Show the invitation dialog and set the name to player
*/
void KBgEngineFIBS::inviteDialog()
{
fibsRequestInvitation("");
}
/*
* Show the invitation dialog and set the name to player
*/
void KBgEngineFIBS::fibsRequestInvitation(const TQString &player)
{
if (!invitationDlg) {
TQString p = player;
invitationDlg = new KBgInvite("invite");
connect(invitationDlg, TQT_SIGNAL(inviteCommand(const TQString &)), this, TQT_SLOT(handleCommand(const TQString &)));
connect(invitationDlg, TQT_SIGNAL(dialogDone()), this, TQT_SLOT(invitationDone()));
}
invitationDlg->setPlayer(player);
invitationDlg->show();
}
/*
* Finish off the invitation dialog
*/
void KBgEngineFIBS::invitationDone()
{
delete invitationDlg;
invitationDlg = 0;
}
// == connection handling ======================================================
/*
* Establish a connection to the server and log in if the parameter login
* is true.
*/
void KBgEngineFIBS::connectFIBS()
{
/*
* Make sure the connection parameter are properly set.
*/
if (!queryConnection(false))
return;
conAction->setEnabled(false);
newAction->setEnabled(false);
disAction->setEnabled(false);
/*
* Connect
*/
emit infoText(i18n("Looking up %1").arg(infoFIBS[FIBSHost]));
connection->connectToHost(infoFIBS[FIBSHost], infoFIBS[FIBSPort].toUShort());
return;
}
/*
* Hostname has been resolved.
*/
void KBgEngineFIBS::hostFound()
{
emit infoText(i18n("Connecting to %1").arg(infoFIBS[FIBSHost]));
}
/*
* An error has occurred. Reset and inform the user.
*/
void KBgEngineFIBS::connError(int f)
{
switch (f) {
case TQSocket::ErrConnectionRefused:
emit infoText(i18n("Error, connection has been refused"));
break;
case TQSocket::ErrHostNotFound:
emit infoText(i18n("Error, nonexistent host or name server down."));
break;
case TQSocket::ErrSocketRead:
emit infoText(i18n("Error, reading data from socket"));
break;
}
connectionClosed();
return;
}
void KBgEngineFIBS::readData()
{
TQString line;
while(connection->canReadLine()) {
line = connection->readLine();
if (line.length() > 2) {
line.truncate(line.length()-2);
handleServerData(line);
}
}
}
/*
* Transmit the string s to the server
*/
void KBgEngineFIBS::sendData(const TQString &s)
{
connection->writeBlock((s+"\r\n").latin1(),2+s.length());
}
/*
* Connection has been established. Log in and update the menus & actions.
*/
void KBgEngineFIBS::connected()
{
conAction->setEnabled(false);
newAction->setEnabled(false);
disAction->setEnabled(true);
menu->setItemEnabled( cmdMenuID, true);
menu->setItemEnabled(respMenuID, true);
menu->setItemEnabled(optsMenuID, true);
/*
* Initialize the rx state machine
*/
rxStatus = RxConnect;
rxCollect = "";
/*
* Depending on whether somebody else wants to handle the login or not
*/
if (login) {
/*
* Make sure the player list is empty when the whole list comes
* right after login
*/
playerlist->clear();
/*
* Login, using the autologin feature of FIBS, before we even receive anything.
*/
TQString entry;
entry.setNum(CLIP_VERSION);
emit serverString(TQString("login ") + PROG_NAME + "-" + PROG_VERSION + " " + entry + " "
+ infoFIBS[FIBSUser] + " " + infoFIBS[FIBSPswd]);
} else {
emit serverString("guest");
login = true;
}
/*
* Some visual feedback and done
*/
emit infoText(i18n("Connected") + "<br>");
}
/*
* Create a new account on FIBS. Obviously, this will also create
* a connection. The actual login is handled in the message parsing
* state machine.
*/
void KBgEngineFIBS::newAccount()
{
if (!queryConnection(true))
return;
rxStatus = RxNewLogin;
rxCollect = "";
login = false;
connectFIBS();
}
/*
* Send a disconnection request to the server. The server will disconnect
* and we will receive a connectionClosed() signal.
*/
void KBgEngineFIBS::disconnectFIBS()
{
// send two lines in case we are stuck in the login phase
emit serverString("quit");
emit serverString("quit");
}
/*
* Connection to the server is closed for some (unknown) reason. Delete
* the connection object and get the actions into a proper state.
*/
void KBgEngineFIBS::connectionClosed()
{
/*
* Read remaining input
*/
readData();
/*
* Flush whatever is left in the rxBuffer and send a note
*/
emit infoText(rxCollect + "<br><hr>");
emit infoText(i18n("Disconnected.") + "<br>");
conAction->setEnabled(true);
newAction->setEnabled(true);
disAction->setEnabled(false);
menu->setItemEnabled(joinMenuID, false);
menu->setItemEnabled( cmdMenuID, false);
menu->setItemEnabled(respMenuID, false);
menu->setItemEnabled(optsMenuID, false);
}
/*
* To establish a connection, we need to query the server name, the port
* number, the login and the password.
*/
bool KBgEngineFIBS::queryConnection(const bool newlogin)
{
TQString text, msg;
bool first, ret = true;
/*
* query the connection parameter
*/
if (newlogin || infoFIBS[FIBSHost].isEmpty()) {
msg = KLineEditDlg::getText(i18n("Enter the name of the server you want to connect to.\n"
"This should almost always be \"fibs.com\"."),
infoFIBS[FIBSHost], &ret, (TQWidget *)parent());
if (ret)
infoFIBS[FIBSHost] = msg;
else
return false;
}
if (newlogin || infoFIBS[FIBSPort].isEmpty()) {
msg = KLineEditDlg::getText(i18n("Enter the port number on the server. "
"It should almost always be \"4321\"."),
infoFIBS[FIBSPort], &ret, (TQWidget *)parent());
if (ret)
infoFIBS[FIBSPort] = msg;
else
return false;
}
if (newlogin || infoFIBS[FIBSUser].isEmpty()) {
if (newlogin)
text = i18n("Enter the login you would like to use on the server %1. The login may not\n"
"contain spaces or colons. If the login you choose is not available, you'll later be\n"
"given the opportunity to pick another one.\n\n").arg(infoFIBS[FIBSHost]);
else
text = i18n("Enter your login on the server %1. If you don't have a login, you\n"
"should create one using the corresponding menu option.\n\n").arg(infoFIBS[FIBSHost]);
first = true;
do {
msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret,
(TQWidget *)parent())).stripWhiteSpace();
if (first) {
text += i18n("The login may not contain spaces or colons!");
first = false;
}
} while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':')));
if (ret)
infoFIBS[FIBSUser] = msg;
else
return false;
}
if (newlogin || infoFIBS[FIBSPswd].isEmpty()) {
if (newlogin)
text = i18n("Enter the password you would like to use with the login %1\n"
"on the server %2. It may not contain colons.\n\n").
arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]);
else
text = i18n("Enter the password for the login %1 on the server %2.\n\n").
arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]);
first = true;
do {
TQCString password;
if (newlogin)
ret = (KPasswordDialog::getNewPassword(password, text) == KPasswordDialog::Accepted);
else
ret = (KPasswordDialog::getPassword(password, text) == KPasswordDialog::Accepted);
password.stripWhiteSpace();
msg = password;
if (first) {
text += i18n("The password may not contain colons or spaces!");
first = false;
}
} while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':')));
if (ret)
infoFIBS[FIBSPswd] = msg;
else
return false;
}
/*
* Made it here, all parameters acquired
*/
return true;
}
// == message parsing ==========================================================
/*
* Pattern setup - rather long and boring
*/
void KBgEngineFIBS::initPattern()
{
TQString pattern;
/*
* Initialize the search pattern array
*/
pat[Welcome] = TQRegExp(pattern.sprintf("^%d ", CLIP_WELCOME));
pat[OwnInfo] = TQRegExp(pattern.sprintf("^%d ", CLIP_OWN_INFO));
pat[WhoInfo] = TQRegExp(pattern.sprintf("^%d ", CLIP_WHO_INFO));
pat[WhoEnde] = TQRegExp(pattern.sprintf("^%d$", CLIP_WHO_END));
pat[MotdBeg] = TQRegExp(pattern.sprintf("^%d" , CLIP_MOTD_BEGIN));
pat[MotdEnd] = TQRegExp(pattern.sprintf("^%d" , CLIP_MOTD_END));
pat[MsgPers] = TQRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE));
pat[MsgDeli] = TQRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_DELIVERED));
pat[MsgSave] = TQRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_SAVED));
pat[ChatSay] = TQRegExp(pattern.sprintf("^%d ", CLIP_SAYS));
pat[ChatSht] = TQRegExp(pattern.sprintf("^%d ", CLIP_SHOUTS));
pat[ChatWis] = TQRegExp(pattern.sprintf("^%d ", CLIP_WHISPERS));
pat[ChatKib] = TQRegExp(pattern.sprintf("^%d ", CLIP_KIBITZES));
pat[SelfSay] = TQRegExp(pattern.sprintf("^%d ", CLIP_YOU_SAY));
pat[SelfSht] = TQRegExp(pattern.sprintf("^%d ", CLIP_YOU_SHOUT));
pat[SelfWis] = TQRegExp(pattern.sprintf("^%d ", CLIP_YOU_WHISPER));
pat[SelfKib] = TQRegExp(pattern.sprintf("^%d ", CLIP_YOU_KIBITZ));
pat[UserLin] = TQRegExp(pattern.sprintf("^%d ", CLIP_LOGIN));
pat[UserLot] = TQRegExp(pattern.sprintf("^%d ", CLIP_LOGOUT));
pat[NoLogin] = TQRegExp("\\*\\* Unknown command: 'login'");
pat[BegRate] = TQRegExp("^rating calculation:$");
pat[EndRate] = TQRegExp("^change for ");
pat[HTML_lt] = TQRegExp("<");
pat[HTML_gt] = TQRegExp(">");
pat[BoardSY] = TQRegExp("^Value of 'boardstyle' set to 3");
pat[BoardSN] = TQRegExp("^Value of 'boardstyle' set to [^3]");
pat[WhoisBG] = TQRegExp("^Information about ");
pat[WhoisE1] = TQRegExp("^ No email address\\.$");
pat[WhoisE2] = TQRegExp("^ Email address: ");
pat[SelfSlf] = TQRegExp("^You say to yourself:");
pat[Goodbye] = TQRegExp("^ Goodbye\\.");
pat[GameSav] = TQRegExp("The game was saved\\.$");
pat[RawBord] = TQRegExp("^board:");
pat[YouTurn] = TQRegExp("^It's your turn\\. Please roll or double");
pat[PlsMove] = TQRegExp("^Please move [1-6]+ pie");
pat[EndWtch] = TQRegExp("^You stop watching ");
pat[BegWtch] = TQRegExp("^You're now watching ");
pat[BegGame] = TQRegExp("^Starting a new game with ");
pat[Reload1] = TQRegExp("^You are now playing with ");
pat[Reload2] = TQRegExp(" has joined you. Your running match was loaded\\.$");
pat[OneWave] = TQRegExp(" waves goodbye.$");
pat[TwoWave] = TQRegExp(" waves goodbye again.$");
pat[YouWave] = TQRegExp("^You wave goodbye.$");
pat[GameBG1] = TQRegExp("start a [0-9]+ point match");
pat[GameBG2] = TQRegExp("start an unlimited match");
pat[GameRE1] = TQRegExp("are resuming their [0-9]+-point match");
pat[GameRE2] = TQRegExp("are resuming their unlimited match");
pat[GameEnd] = TQRegExp("point match against");
pat[TabChar] = TQRegExp("\\t");
pat[PlsChar] = TQRegExp("\\+");
pat[Invite0] = TQRegExp(" wants to play a [0-9]+ point match with you\\.$");
pat[Invite1] = TQRegExp("^.+ wants to play a ");
pat[Invite2] = TQRegExp(" wants to resume a saved match with you\\.$");
pat[Invite3] = TQRegExp(" wants to play an unlimited match with you\\.$");
pat[TypJoin] = TQRegExp("^Type 'join ");
pat[OneName] = TQRegExp("^ONE USERNAME PER PERSON ONLY!!!");
pat[YouAway] = TQRegExp("^You're away. Please type 'back'");
pat[YouBack] = TQRegExp("^Welcome back\\.$");
pat[YouMove] = TQRegExp("^It's your turn to move\\.");
pat[YouRoll] = TQRegExp("^It's your turn to roll or double\\.");
pat[TwoStar] = TQRegExp("^\\*\\* ");
pat[OthrNam] = TQRegExp("^\\*\\* Please use another name\\. ");
pat[BoxHori] = TQRegExp("^ *\\+-*\\+ *$");
pat[BoxVer1] = TQRegExp("^ *\\|");
pat[BoxVer2] = TQRegExp("\\| *$");
pat[YourNam] = TQRegExp("Your name will be ");
pat[GivePwd] = TQRegExp("Please give your password:");
pat[RetypeP] = TQRegExp("Please retype your password:");
pat[HelpTxt] = TQRegExp("^NAME$");
pat[MatchB1] = TQRegExp(" has joined you for a [0-9]+ point match\\.$");
pat[MatchB2] = TQRegExp(" has joined you for an unlimited match\\.$");
pat[EndLose] = TQRegExp(" wins the [0-9]+ point match [0-9]+-[0-9]+");
pat[EndVict] = TQRegExp(" win the [0-9]+ point match [0-9]+-[0-9]+");
pat[RejAcpt] = TQRegExp("Type 'accept' or 'reject'\\.$");
pat[YouAcpt] = TQRegExp("^You accept the double\\. The cube shows [0-9]+\\.");
pat[KeepAlv] = TQRegExp("^\\*\\* Unknown command: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'");
pat[RatingY] = TQRegExp("You'll see how the rating changes are calculated\\.$");
pat[RatingN] = TQRegExp("You won't see how the rating changes are calculated\\.$");
// FIXME same problem as in previous line
// mpgnu accepts the double.5 arthur_tn - gnu 1 0 1243.32 365 6 983722411 adsl-61-168-141.bna.bellsouth.net - -
// FIXME: <PLAYER> can't move. -- needs board reload...
/*
opponent matchlength score (your points first)
**gnu 1 0 - 0
*blah 1 0 - 0
kraut 1 0 - 0
logged in and ready **
logged in *
otherwise " "
*/
pat[ConLeav] = TQRegExp("^Type 'join' if you want to play the next game, type 'leave' if you don't\\.$");
pat[GreedyY] = TQRegExp("^\\*\\* Will use automatic greedy bearoffs\\.");
pat[GreedyN] = TQRegExp("^\\*\\* Won't use automatic greedy bearoffs\\.");
pat[BegBlnd] = TQRegExp("^\\*\\* You blind ");
pat[EndBlnd] = TQRegExp("^\\*\\* You unblind ");
pat[MatchB3] = TQRegExp("^\\*\\* You are now playing a [0-9]+ point match with ");
pat[MatchB4] = TQRegExp("^\\*\\* You are now playing an unlimited match with ");
pat[RejCont] = TQRegExp("^You reject\\. The game continues\\.");
pat[AcptWin] = TQRegExp("^You accept and win ");
pat[YouGive] = TQRegExp("^You give up\\.");
pat[DoubleY] = TQRegExp("^\\*\\* You will be asked if you want to double\\.");
pat[DoubleN] = TQRegExp("^\\*\\* You won't be asked if you want to double\\.");
}
/*
* Parse an incoming line and notify all interested parties - first match
* decides.
*/
void KBgEngineFIBS::handleServerData(TQString &line)
{
TQString rawline = line; // contains the line before it is HTML'fied
/*
* Fix-up any HTML-like tags in the line
*/
line.replace(pat[HTML_lt], "&lt;");
line.replace(pat[HTML_gt], "&gt;");
/*
* FIBS sometimes sends tabs, where it should send 8 spaces...
*/
line.replace(pat[TabChar], " ");
switch (rxStatus) {
case RxConnect:
handleMessageConnect(line, rawline);
break;
case RxMotd:
handleMessageMotd(line);
return;
case RxWhois:
handleMessageWhois(line);
break;
case RxRating:
handleMessageRating(line);
break;
case RxNewLogin:
handleMessageNewLogin(line);
break;
case RxIgnore:
/*
* Ignore _ALL_ incoming strings - this is needed during the
* login phase, when the message box is open.
*/
break;
case RxGoodbye:
/*
* Receive the logout sequence. The string will be flushed by the
* disconnectFIBS() callback
*/
rxCollect += TQString("<font color=\"blue\"><pre>") + line + "</pre></font><br>";
break;
case RxNormal:
handleMessageNormal(line, rawline);
break;
default:
/*
* This is a serious problem - latin1() is fine since the line comes from FIBS.
*/
std::cerr << "PROBLEM in KBgEngineFIBS::handleServerData: " << line.latin1() << std::endl;
}
}
/*
* Handle messages during the RxWhois state
*/
void KBgEngineFIBS::handleMessageWhois(const TQString &line)
{
rxCollect += "<br>&nbsp;&nbsp;&nbsp;&nbsp;" + line;
if (line.contains(pat[WhoisE1]) || line.contains(pat[WhoisE2])) {
rxStatus = RxNormal;
emit infoText("<font color=\"darkgreen\">" + rxCollect + "<br></font>");
}
}
/*
* Handle messages during the RxRating state
*/
void KBgEngineFIBS::handleMessageRating(const TQString &line)
{
rxCollect += "<br>" + line;
if (line.contains(pat[EndRate]) && ++rxCount == 2) {
emit infoText("<font color=\"blue\">" + rxCollect + "<br></font>");
rxStatus = RxNormal;
}
}
/*
* Handle messages during the RxMotd state
*/
void KBgEngineFIBS::handleMessageMotd(const TQString &line)
{
if (line.contains(pat[MotdEnd])) {
rxStatus = RxNormal;
emit infoText("<font color=\"blue\"><pre>" + rxCollect + "</pre></font>");
/*
* just to be on the safe side, we set the value of boardstyle.
* we do it here, since this is reasonably late in the login
* procedure
*/
emit serverString("set boardstyle 3");
} else {
TQString tline = line;
tline.replace(pat[BoxHori], "<br><hr>");
tline.replace(pat[BoxVer1], "");
tline.replace(pat[BoxVer2], "");
rxCollect += "<br>" + tline;
}
}
/*
* Handle messages during the RxConnect state
*/
void KBgEngineFIBS::handleMessageConnect(const TQString &line, const TQString &rawline)
{
/*
* Two possibilities: either we are logged in or we sent bad password/login
*/
if (line.contains("login:")) {
/*
* This can only happen if the password/login is wrong.
*/
if (rxCollect.isEmpty()) {
rxStatus = RxIgnore;
int ret = KMessageBox::warningContinueCancel
((TQWidget *)parent(), i18n("There was a problem with "
"your login and password. "
"You can reenter\n"
"your login and password and "
"try to reconnect."),
i18n("Wrong Login/Password"),
i18n("Reconnect"));
if (ret == KMessageBox::Continue) {
infoFIBS[FIBSUser] = "";
infoFIBS[FIBSPswd] = "";
login = true;
connectFIBS(); // will reset the rxStatus
} else {
rxStatus = RxConnect;
emit serverString("");
emit serverString("");
}
return;
}
emit infoText("<hr><pre>" + rxCollect + "</pre><br>");
rxCollect = "";
return;
}
/*
* Ok, we are logged in! Now receive personal information. These
* are completely useless but what the heck.
*/
if (line.contains(pat[Welcome])) {
char p[3][256];
time_t tmp;
// Using latin1() is okay, since the string comes from FIBS.
int words = sscanf (line.latin1(), "%255s%255s%li%255s", p[0], p[1], &tmp, p[2]);
if (words >= 4) {
TQDateTime d; d.setTime_t(tmp);
TQString text = i18n("%1, last logged in from %2 at %3.").arg(p[1]).arg(p[2]).arg(d.toString());
emit infoText("<hr><br>" + text);
playerlist->setName(p[1]);
}
return;
}
/*
* Initial parsing of user options and making sure that settings needed
* by us are at the correct value. We use and ignore values according
* to the following list:
*
* p[ 0] - CLIP_OWN_INFO
* p[ 1] - name -- IGNORE
* OptAllowPip
* n[ 0] - autoboard -- IGNORE
* OptAutoDouble
* OptAutoMove
* n[ 1] - away -- IGNORE
* n[ 2] - bell -- IGNORE
* OptCrawford
* n[ 3] - double -- IGNORE
* n[ 4] - expierience -- IGNORE
* OptGreedy
* n[ 6] - moreboards -- IGNORE and set to YES
* OptMoves
* n[ 8] - notify -- IGNORE and set to YES
* rating - rating -- IGNORE
* OptRatings
* OptReady
* n[10] - redoubles -- IGNORE
* n[11] - report -- IGNORE and set to YES
* OptSilent
* p[3] - timezone
*
*/
if (line.contains(pat[OwnInfo])) {
rxStatus = RxNormal;
int fibsOptions[NumFIBSOpt];
char p[3][256];
int n[12];
double rating;
// Using latin1() is okay, since the string comes from FIBS.
int words = sscanf (line.latin1(), "%255s%255s%i%i%i%i%i%i%i%i%i%i%i%i%i%lf%i%i%i%i%i%255s",
p[0], p[1],
&fibsOptions[OptAllowPip],
&n[0],
&fibsOptions[OptDouble],
&fibsOptions[OptAutoMove], // equivalent to OptDouble, can be ignored
&n[1], &n[2],
&fibsOptions[OptCrawford],
&n[3], &n[4],
&fibsOptions[OptGreedy],
&n[6],
&fibsOptions[OptMoves],
&n[8],
&rating,
&fibsOptions[OptRatings],
&fibsOptions[OptReady],
&n[10], &n[11],
&fibsOptions[OptSilent],
p[2]);
if (words >= 22 && n[6] != 1) {
/*
* need to get boards after new dice have arrived
*/
emit infoText("<font color=\"red\">" + i18n("The moreboards toggle has been set.") + "</font>");
emit serverString("toggle moreboards");
}
if (words >= 22 && n[8] != 1) {
/*
* need to know who logs out
*/
emit infoText("<font color=\"red\">" + i18n("The notify toggle has been set.") + "</font>");
emit serverString("toggle notify");
}
if (words >= 22 && n[11] != 1) {
/*
* want to know who starts playing games
*/
emit infoText("<font color=\"red\">" + i18n("The report toggle has been set.") + "</font>");
emit serverString("toggle report");
}
/*
* Set the correct toggles in the options menu
*/
fibsOpt[OptReady]->setChecked(fibsOptions[OptReady]);
fibsOpt[OptDouble]->setChecked(!fibsOptions[OptDouble]);
fibsOpt[OptRatings]->setChecked(fibsOptions[OptRatings]);
return;
}
/*
* The beginning of a new login procedure starts starts here
*/
if (line.contains(pat[OneName])) {
rxStatus = RxNewLogin;
emit infoText(TQString("<font color=\"red\">") + rxCollect + "</font>");
rxCollect = "";
TQString tmp = rawline;
handleServerData(tmp);
return;
}
/*
* Still in the middle of the login sequence, still collecting information
*/
rxCollect += "<br>" + line;
}
/*
* Handle messages during the RxNewLogin state
*/
void KBgEngineFIBS::handleMessageNewLogin(const TQString &line)
{
/*
* Request the new login
*/
if (line.contains(pat[OneName])) {
emit serverString(TQString("name ") + infoFIBS[FIBSUser]);
return;
}
/*
* Ooops, user name already exists
*/
if (line.contains(pat[OthrNam])) {
TQString text = i18n("The selected login is alreay in use! Please select another one.");
bool ret, first = true;
TQString msg;
do {
msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret,
(TQWidget *)parent())).stripWhiteSpace();
if (first) {
text += i18n("\n\nThe login may not contain spaces or colons!");
first = false;
}
} while (msg.contains(' ') || msg.contains(':'));
if (ret) {
infoFIBS[FIBSUser] = msg;
emit serverString("name " + msg);
} else
emit serverString("bye");
return;
}
/*
* first time we send the password
*/
if (line.contains(pat[YourNam])) {
emit serverString(infoFIBS[FIBSPswd]);
return;
}
/*
* second time we send the password
*/
if (line.contains(pat[GivePwd])) {
emit serverString(infoFIBS[FIBSPswd]);
return;
}
/*
* at this point we are done creating the account
*/
if (line.contains(pat[RetypeP])) {
TQString text = i18n("Your account has been created. Your new login is <u>%1</u>. To fully activate "
"this account, I will now close the connection. Once you reconnect, you can start "
"playing backgammon on FIBS.").arg(infoFIBS[FIBSUser]);
emit infoText("<br><hr><font color=\"blue\">" + text + "</font><br><hr>");
emit serverString("bye");
rxStatus = RxNormal;
rxCollect = "";
return;
}
return;
}
/*
* Handle all normal messages - during the RxNormal state
*/
void KBgEngineFIBS::handleMessageNormal(TQString &line, TQString &rawline)
{
// - ignored ----------------------------------------------------------------------
/*
* For now, the waves are ignored. They should probably go into
* the chat window -- but only optional
*/
if (line.contains(pat[OneWave]) || line.contains(pat[TwoWave]) || line.contains(pat[YouWave])) {
return;
}
/*
* These messages used to go into the games window. If KBackgammon
* ever gets a games window, they should be in there. For now, they
* are ignored.
*/
else if (line.contains(pat[GameBG1]) || line.contains(pat[GameBG2]) || line.contains(pat[GameRE1]) ||
line.contains(pat[GameRE2]) || line.contains(pat[GameEnd])) {
return;
}
/*
* Artefact caused by the login test procedure utilized.
*/
else if (line.contains(pat[NoLogin])) {
return;
}
/*
* Connection keep-alive response
*/
else if (line.contains(pat[KeepAlv])) {
return;
}
// --------------------------------------------------------------------------------
/*
* Chat and personal messages - note that the chat window sends these messages
* back to us so we can display them if the user wants that.
*/
else if (line.contains(pat[ChatSay]) || line.contains(pat[ChatSht]) || line.contains(pat[ChatWis]) ||
line.contains(pat[ChatKib]) || line.contains(pat[SelfSay]) || line.contains(pat[SelfSht]) ||
line.contains(pat[SelfWis]) || line.contains(pat[SelfKib]) || line.contains(pat[SelfSlf]) ||
line.contains(pat[MsgPers]) || line.contains(pat[MsgDeli]) || line.contains(pat[MsgSave])) {
emit chatMessage(line);
return;
}
// --------------------------------------------------------------------------------
/*
* Beginning of games. In all these cases we are playing and not watching.
*/
else if (line.contains(pat[MatchB1]) || line.contains(pat[MatchB2])) {
if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty())
emit serverString("kibitz " + autoMsg[MsgBeg]);
}
else if (line.contains(pat[MatchB3]) || line.contains(pat[MatchB4])) {
if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty())
emit serverString("kibitz " + autoMsg[MsgBeg]);
line = "<font color=\"red\">" + line + "</font>";
}
// --------------------------------------------------------------------------------
/*
* The help should be handled separately. A fairly complete implementation of a
* help parsing can be found in KFibs.
*/
else if (line.contains(pat[HelpTxt])) {
// do nothing
}
// --------------------------------------------------------------------------------
/*
* Simple cases without the need for many comments...
*/
else if (line.contains(pat[RawBord])) {
/*
* Save the board string and create a new game state
*/
KBgStatus *st = new KBgStatus(currBoard = rawline);
/*
* Save important state data and stop the timeout
*/
ct->stop();
undoCounter = 0;
pname[US ] = st->player(US);
pname[THEM] = st->player(THEM);
playing = (TQString("You") == pname[US]);
toMove = st->moves();
/*
* Update the caption string
*/
if (st->turn() < 0)
caption = i18n("%1 (%2) vs. %3 (%4) - game over").arg(pname[US]).
arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM));
else if (st->length() < 0)
caption = i18n("%1 (%2) vs. %3 (%4) - unlimited match").arg(pname[US]).
arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM));
else
caption = i18n("%1 (%2) vs. %3 (%4) - %5 point match").arg(pname[US]).
arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)).
arg(st->length());
emit statText(caption);
/*
* Emit information and drop the state object
*/
emit allowMoving(playing && (st->turn() == US));
emit newState(*st);
delete st;
/*
* Set the actions correctly
*/
emit allowCommand(Load, true );
emit allowCommand(Undo, false);
emit allowCommand(Redo, false);
emit allowCommand(Done, false);
return;
}
else if (line.contains(pat[PlsMove]) || line.contains(pat[YouMove])) {
KNotifyClient::event("move", i18n("Please make your move"));
}
// --------------------------------------------------------------------------------
/*
* Being away and coming back
*/
else if (line.contains(pat[YouAway])) {
emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, true);
actBack->setEnabled(true);
line += "<br><pre> </pre>" + i18n("(or use the corresponding menu entry to join the match)");
}
else if (line.contains(pat[YouBack])) {
emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, false);
actBack->setEnabled(false);
actAway->setEnabled(true);
}
// --------------------------------------------------------------------------------
/*
* Catch the response of the user responding to double or resign
*/
else if (line.contains(pat[YouGive]) || line.contains(pat[RejCont]) || line.contains(pat[AcptWin])) {
actAccept->setEnabled(false);
actReject->setEnabled(false);
}
// --------------------------------------------------------------------------------
/*
* Catch the responses to newly set toggles
*/
else if (line.contains(pat[GreedyY]) || line.contains(pat[GreedyN])) {
fibsOpt[OptGreedy]->setChecked(line.contains(pat[GreedyY]));
line = "<font color=\"red\">" + line + "</font>";
}
else if (line.contains(pat[DoubleY]) || line.contains(pat[DoubleN])) {
fibsOpt[OptDouble]->setChecked(line.contains(pat[DoubleY]));
line = "<font color=\"red\">" + line + "</font>";
}
else if (line.contains(pat[RatingY]) || line.contains(pat[RatingN])) {
fibsOpt[OptRatings]->setChecked(line.contains(pat[RatingY]));
line = "<font color=\"red\">" + line + "</font>";
}
// --------------------------------------------------------------------------------
/*
* It's our turn to roll or double
*/
else if (line.contains(pat[YouTurn]) || line.contains(pat[YouRoll])) {
emit allowCommand(Cube, playing);
emit allowCommand(Roll, playing);
emit statText(caption); // force a pip count recomputation by the board
KNotifyClient::event("roll or double", i18n("It's your turn to roll the dice or double the cube"));
}
// --------------------------------------------------------------------------------
/*
* Got an invitation for a match
*/
else if (line.contains(pat[Invite0]) || line.contains(pat[Invite2]) || line.contains(pat[Invite3])) {
rxCollect = rawline.left(rawline.find(' '));
emit serverString("rawwho " + rxCollect);
if (line.contains(pat[Invite0])) {
rawline.replace(pat[Invite1], "");
rawline = rxCollect + " "+ rawline.left(rawline.find(' '));
} else if (line.contains(pat[Invite2])) {
rawline = rxCollect + " r";
} else if (line.contains(pat[Invite3])) {
invitations += rxCollect + " u";
}
invitations += rawline;
return; // will be printed once the rawwho is received
}
// - rx status changes ------------------------------------------------------------
else if (line.contains(pat[WhoisBG])) {
rxStatus = RxWhois;
rxCollect = TQString("<br><u>") + line + "</u>";
return;
}
else if (line.contains(pat[MotdBeg])) {
rxStatus = RxMotd;
rxCollect = "";
return;
}
else if (line.contains(pat[BegRate])) {
rxStatus = RxRating;
rxCount = 0;
rxCollect = "<br>" + line;
return;
}
else if (line.contains(pat[Goodbye])) {
rxStatus = RxGoodbye;
rxCollect = "<br><hr><br>";
handleServerData(rawline); // danger: recursion!
return;
}
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
/*
* Continue a mutli game match? We have to either leave or continue
*/
else if (line.contains(pat[ConLeav])) {
actConti->setEnabled(true);
actLeave->setEnabled(true);
line.append("<br><pre> </pre>" + i18n("(or use the corresponding menu "
"entry to leave or continue the match)"));
}
/*
* Beginning and end of user updates
*/
else if (line.contains(pat[WhoInfo])) {
rawline.replace(pat[WhoInfo], "");
if (rawline.contains(TQRegExp("^" + infoFIBS[FIBSUser] + " "))) {
int ready;
// Using latin1() is fine, since the string is coming from FIBS.
sscanf(rawline.latin1(), "%*s %*s %*s %i %*s %*s %*s %*s %*s %*s %*s %*s", &ready);
fibsOpt[OptReady]->setChecked(ready);
}
emit fibsWhoInfo(rawline);
return;
}
else if (line.contains(pat[WhoEnde])) {
emit fibsWhoEnd();
return;
}
/*
* This message is ignored. The instruction is given elsewhere (and slightly
* delayed in the flow of time).
*/
if (line.contains(pat[TypJoin])) {
return;
}
/*
* Watching other players
*/
else if (line.contains(pat[BegWtch])) {
emit allowCommand(Load, true);
rawline.replace(pat[BegWtch], "");
rawline.truncate(rawline.length()-1);
emit fibsStartNewGame(rawline);
load();
}
else if (line.contains(pat[EndWtch])) {
emit gameOver();
}
/*
* Blinding of players, the actual blind is handled by
* the player list
*/
else if (line.contains(pat[BegBlnd])) {
rawline.replace(pat[BegBlnd], "");
rawline.truncate(rawline.length()-1);
emit changePlayerStatus(rawline, KFibsPlayerList::Blind, true);
line = "<font color=\"red\">" + line + "</font>";
}
else if (line.contains(pat[EndBlnd])) {
rawline.replace(pat[EndBlnd], "");
rawline.truncate(rawline.length()-1);
emit changePlayerStatus(rawline, KFibsPlayerList::Blind, false);
line = "<font color=\"red\">" + line + "</font>";
}
/*
* Starting or reloading games or matches
*/
else if (line.contains(pat[BegGame])) {
rawline.replace(pat[BegGame], "");
rawline.truncate(rawline.length()-1);
emit fibsStartNewGame(rawline);
fibsOpt[OptDouble]->setChecked(true);
fibsOpt[OptGreedy]->setChecked(false);
actConti->setEnabled(false);
actLeave->setEnabled(false);
}
else if (line.contains(pat[Reload1])) {
rawline.replace(pat[Reload1], "");
rawline = rawline.left(rawline.find(' '));
rawline.truncate(rawline.length()-1);
emit fibsStartNewGame(rawline);
fibsOpt[OptDouble]->setChecked(true);
fibsOpt[OptGreedy]->setChecked(false);
actConti->setEnabled(false);
actLeave->setEnabled(false);
load();
}
else if (line.contains(pat[Reload2])) {
rawline.replace(pat[Reload2], "");
emit fibsStartNewGame(rawline);
fibsOpt[OptDouble]->setChecked(true);
fibsOpt[OptGreedy]->setChecked(false);
actConti->setEnabled(false);
actLeave->setEnabled(false);
load();
}
/*
* Opponent offered resignation or the cube. We have to accept
* or reject the offer.
*/
else if (line.contains(pat[RejAcpt])) {
actAccept->setEnabled(true);
actReject->setEnabled(true);
line += "<br><pre> </pre>" + i18n("(or use the corresponding menu "
"entry to accept or reject the offer)");
}
/*
* This is strange: FIBS seems to not send a newline at the
* end of this pattern. So we work around that.
*/
else if (line.contains(pat[YouAcpt])) {
actAccept->setEnabled(false);
actReject->setEnabled(false);
rawline.replace(pat[YouAcpt], "");
line.truncate(line.length()-rawline.length());
if (!rawline.stripWhiteSpace().isEmpty()) {
handleServerData(rawline);
}
}
/*
* Ending of games
*/
else if (line.contains(pat[EndLose])) {
if (playing) {
KNotifyClient::event("game over l", i18n("Sorry, you lost the game."));
if (useAutoMsg[MsgLos] && !autoMsg[MsgLos].stripWhiteSpace().isEmpty())
emit serverString(TQString("tell ") + pname[THEM] + " " + autoMsg[MsgLos]);
}
emit gameOver();
}
else if (line.contains(pat[EndVict])) {
if (playing) {
KNotifyClient::event("game over w", i18n("Congratulations, you won the game!"));
if (useAutoMsg[MsgWin] && !autoMsg[MsgWin].stripWhiteSpace().isEmpty())
emit serverString(TQString("tell ") + pname[THEM] + " " + autoMsg[MsgWin]);
}
emit gameOver();
}
else if (line.contains(pat[GameSav])) {
emit gameOver();
}
/*
* User logs out. This has to be signalled to the player
* list. Get the true user names by working on the rawline.
*/
else if (line.contains(pat[UserLot])) {
rawline.replace(pat[UserLot], "");
emit fibsLogout(rawline.left(rawline.find(' ')));
return;
}
/*
* Emit the name of the newly logged in user.
*/
else if (line.contains(pat[UserLin])) {
rawline.replace(pat[UserLin], "");
emit fibsLogin(rawline.left(rawline.find(' ')));
return;
}
/*
* Special attention has to be paid to the proper setting of
* the 'boardstyle' variable, since we will not be able to display
* the board properly without it.
*/
else if (line.contains(pat[BoardSY])) {
// ignored
return;
}
else if (line.contains(pat[BoardSN])) {
emit serverString("set boardstyle 3");
emit infoText(TQString("<font color=\"red\"><br>")
+ i18n("You should never set the 'boardstyle' variable "
"by hand! It is vital for proper functioning of "
"this program that it remains set to 3. It has "
"been reset for you.")
+ "<br></font>");
return;
}
/*
* This is the final fall through: if the line started with ** and
* hasn't been processed, make it red, since it is a server resp.
* to something we just did.
*/
else if (line.contains(pat[TwoStar])) {
line = "<font color=\"red\">" + line + "</font>";
}
// --------------------------------------------------------------------------------
/*
* Print whatever part of the line made it here
*/
emit infoText(line);
}
// EOF
// == constructor, destructor and setup ========================================
/*
* Constructor
*/
KBgEngineFIBS::KBgEngineFIBS(TQWidget *parent, TQString *name, TQPopupMenu *pmenu)
: KBgEngine(parent, name, pmenu)
{
/*
* No connection, not playing, ready for login
*/
connection = new TQSocket(TQT_TQOBJECT(parent), "fibs connection");
playing = false;
login = true;
connect(connection, TQT_SIGNAL(hostFound()), this, TQT_SLOT(hostFound()));
connect(connection, TQT_SIGNAL(connected()), this, TQT_SLOT(connected()));
connect(connection, TQT_SIGNAL(error(int)), this, TQT_SLOT(connError(int)));
connect(connection, TQT_SIGNAL(connectionClosed()), this, TQT_SLOT(connectionClosed()));
connect(connection, TQT_SIGNAL(delayedCloseFinished()), this, TQT_SLOT(connectionClosed()));
connect(connection, TQT_SIGNAL(readyRead()), this, TQT_SLOT(readData()));
connect(this, TQT_SIGNAL(serverString(const TQString &)), this, TQT_SLOT(sendData(const TQString &)));
/*
* No invitation dialog
*/
invitationDlg = 0;
connect(this, TQT_SIGNAL(fibsWhoInfo(const TQString &)), this, TQT_SLOT(changeJoin(const TQString &)));
connect(this, TQT_SIGNAL(fibsLogout (const TQString &)), this, TQT_SLOT(cancelJoin(const TQString &)));
connect(this, TQT_SIGNAL(gameOver()), this, TQT_SLOT(endGame()));
/*
* Creating, initializing and connecting the player list
*/
playerlist = new KFibsPlayerList(0, "fibs player list");
connect(this, TQT_SIGNAL(fibsWhoInfo(const TQString &)), playerlist, TQT_SLOT(changePlayer(const TQString &)));
connect(this, TQT_SIGNAL(fibsLogout (const TQString &)), playerlist, TQT_SLOT(deletePlayer(const TQString &)));
connect(this, TQT_SIGNAL(fibsWhoEnd()), playerlist, TQT_SLOT(stopUpdate()));
connect(this, TQT_SIGNAL(fibsConnectionClosed()), playerlist, TQT_SLOT(stopUpdate()));
connect(this, TQT_SIGNAL(changePlayerStatus(const TQString &, int, bool)),
playerlist, TQT_SLOT(changePlayerStatus(const TQString &, int, bool)));
connect(playerlist, TQT_SIGNAL(fibsCommand(const TQString &)), this, TQT_SLOT(handleCommand(const TQString &)));
connect(playerlist, TQT_SIGNAL(fibsInvite(const TQString &)), this, TQT_SLOT(fibsRequestInvitation(const TQString &)));
/*
* Create, initialize and connect the chat window
*/
chatWindow = new KBgChat(0, "chat window");
connect(this, TQT_SIGNAL(chatMessage(const TQString &)), chatWindow, TQT_SLOT(handleData(const TQString &)));
connect(this, TQT_SIGNAL(fibsStartNewGame(const TQString &)), chatWindow, TQT_SLOT(startGame(const TQString &)));
connect(this, TQT_SIGNAL(gameOver()), chatWindow, TQT_SLOT(endGame()));
connect(this, TQT_SIGNAL(fibsLogout (const TQString &)), chatWindow, TQT_SLOT(deletePlayer(const TQString &)));
connect(chatWindow, TQT_SIGNAL(fibsCommand(const TQString &)), this, TQT_SLOT(handleCommand(const TQString &)));
connect(chatWindow, TQT_SIGNAL(fibsRequestInvitation(const TQString &)), this, TQT_SLOT(fibsRequestInvitation(const TQString &)));
connect(chatWindow, TQT_SIGNAL(personalMessage(const TQString &)), this, TQT_SLOT(personalMessage(const TQString &)));
connect(playerlist, TQT_SIGNAL(fibsTalk(const TQString &)), chatWindow, TQT_SLOT(fibsTalk(const TQString &)));
/*
* Creating, initializing and connecting the menu
* ----------------------------------------------
*/
respMenu = new TQPopupMenu();
joinMenu = new TQPopupMenu();
cmdMenu = new TQPopupMenu();
optsMenu = new TQPopupMenu();
/*
* Initialize the FIBS submenu - this is also put in the play menu
*/
conAction = new KAction(i18n("&Connect"), 0, this, TQT_SLOT( connectFIBS()), this);
newAction = new KAction(i18n("New Account"), 0, this, TQT_SLOT( newAccount()), this);
disAction = new KAction(i18n("&Disconnect"), 0, this, TQT_SLOT(disconnectFIBS()), this);
conAction->setEnabled(true ); conAction->plug(menu);
disAction->setEnabled(false); disAction->plug(menu);
newAction->setEnabled(true ); newAction->plug(menu);
menu->insertSeparator();
(invAction = new KAction(i18n("&Invite..."), 0, this, TQT_SLOT(inviteDialog()), this))->plug(menu);
/*
* Create and fill the response menu. This is for all these: type this or
* that messages from FIBS.
*/
cmdMenuID = menu->insertItem(i18n("&Commands"), cmdMenu); {
(actAway = new KAction(i18n("Away"), 0, this, TQT_SLOT(away()), this))->plug(cmdMenu);
(actBack = new KAction(i18n("Back"), 0, this, TQT_SLOT(back()), this))->plug(cmdMenu);
actAway->setEnabled(true);
actBack->setEnabled(false);
}
/*
* Create the server side options. This is preliminary and needs more work.
* The available options are skewed, since they refelect the needs of the
* author. Contact jens@hoefkens.com if your favorite option is missing.
*/
optsMenuID = menu->insertItem(i18n("&Options"), optsMenu); {
for (int i = 0; i < NumFIBSOpt; i++)
fibsOpt[i] = 0;
fibsOpt[OptReady] = new KToggleAction(i18n("Ready to Play"),
0, this, TQT_SLOT(toggle_ready()), this);
fibsOpt[OptRatings] = new KToggleAction(i18n("Show Rating Computations"),
0, this, TQT_SLOT(toggle_ratings()), this);
fibsOpt[OptRatings]->setCheckedState(i18n("Hide Rating Computations"));
fibsOpt[OptGreedy] = new KToggleAction(i18n("Greedy Bearoffs"),
0, this, TQT_SLOT(toggle_greedy()), this);
fibsOpt[OptDouble] = new KToggleAction(i18n("Ask for Doubles"),
0, this, TQT_SLOT(toggle_double()), this);
for (int i = 0; i < NumFIBSOpt; i++)
if (fibsOpt[i])
fibsOpt[i]->plug(optsMenu);
}
/*
* Create and fill the response menu. This is for all these: type this or
* that messages from FIBS.
*/
respMenuID = menu->insertItem(i18n("&Response"), respMenu); {
(actAccept = new KAction(i18n("Accept"), 0, this, TQT_SLOT(accept()), this))->plug(respMenu);
(actReject = new KAction(i18n("Reject"), 0, this, TQT_SLOT(reject()), this))->plug(respMenu);
actAccept->setEnabled(false);
actReject->setEnabled(false);
respMenu->insertSeparator();
(actConti = new KAction(i18n("Join"), 0, this, TQT_SLOT(match_conti()), this))->plug(respMenu);
(actLeave = new KAction(i18n("Leave"), 0, this, TQT_SLOT(match_leave()), this))->plug(respMenu);
actConti->setEnabled(false);
actLeave->setEnabled(false);
}
/*
* Create the join menu and do not fill it (this happens at first
* action setup.
*/
joinMenuID = menu->insertItem(i18n("&Join"), joinMenu); {
numJoin = -1;
actJoin[0] = new KAction("", 0, this, TQT_SLOT(join_0()), this);
actJoin[1] = new KAction("", 0, this, TQT_SLOT(join_1()), this);
actJoin[2] = new KAction("", 0, this, TQT_SLOT(join_2()), this);
actJoin[3] = new KAction("", 0, this, TQT_SLOT(join_3()), this);
actJoin[4] = new KAction("", 0, this, TQT_SLOT(join_4()), this);
actJoin[5] = new KAction("", 0, this, TQT_SLOT(join_5()), this);
actJoin[6] = new KAction("", 0, this, TQT_SLOT(join_6()), this);
actJoin[7] = new KAction("", 0, this, TQT_SLOT(join_7()), this);
}
menu->setItemEnabled(joinMenuID, false);
menu->setItemEnabled( cmdMenuID, false);
menu->setItemEnabled(respMenuID, false);
menu->setItemEnabled(optsMenuID, false);
/*
* Continue with the FIBS menu
*/
menu->insertSeparator();
(listAct = new KToggleAction(i18n("&Player List"), 0, this, TQT_SLOT(showList()), this))->plug(menu);
(chatAct = new KToggleAction(i18n("&Chat"), 0, this, TQT_SLOT(showChat()), this))->plug(menu);
connect(playerlist, TQT_SIGNAL(windowVisible(bool)), listAct, TQT_SLOT(setChecked(bool)));
connect(chatWindow, TQT_SIGNAL(windowVisible(bool)), chatAct, TQT_SLOT(setChecked(bool)));
/*
* Create message IDs. This sets up a lot of regular expressions.
*/
initPattern();
/*
* Restore old settings
*/
readConfig();
// FIXME: open the child windows in start() and not here
/*
* Update the menu actions
*/
listAct->setChecked(playerlist->isVisible());
chatAct->setChecked(chatWindow->isVisible());
/*
* Initialize the keepalive timer FIXME: make this a setting
*/
keepalive = true;
// FIXME: move the start to connect...
keepaliveTimer = new TQTimer(this);
connect(keepaliveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(keepAlive()));
keepaliveTimer->start(1200000);
}
/*
* Destructor deletes child objects if necessary
*/
KBgEngineFIBS::~KBgEngineFIBS()
{
delete joinMenu;
delete respMenu;
delete cmdMenu;
delete optsMenu;
delete connection;
delete invitationDlg;
delete playerlist;
delete chatWindow;
}