# HG changeset patch # User jose1711 # Date 1293122870 -3600 # Node ID 5ef5415c4ee1240891de4ce0513dab4c1de50941 # Parent 467ab06858901d326bea47e7b3b4fa2b4465fa75# Parent 4ba4f021070f2a046af9814ee44e6fd8cf094fd9 Updated Slovak translation + merge diff -r 467ab0685890 -r 5ef5415c4ee1 .hgtags --- a/.hgtags Tue Dec 14 22:32:47 2010 +0100 +++ b/.hgtags Thu Dec 23 17:47:50 2010 +0100 @@ -23,3 +23,4 @@ 3620607258cdc1213dce20cb6ad7872f6b8085e0 Hedgewars-iOS-1.0.1 adffb668f06e265b45d1e4aedc283e6f4e5ba7e8 Hedgewars-iOS-1.1 ede569bb76f389bd5dfbb7ebf68af3087e3e881c Hedgewars-iOS-1.2 +a5735e877aae61cd705265e2f8c0c7ad08d45f0e Hedgewars-iOS-1.2.1 diff -r 467ab0685890 -r 5ef5415c4ee1 CMakeLists.txt diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/SquareLabel.cpp --- a/QTfrontend/SquareLabel.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/SquareLabel.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -29,6 +29,8 @@ void SquareLabel::paintEvent(QPaintEvent * event) { + Q_UNUSED(event); + QPainter painter(this); int pixsize; if (width() > height()) { diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/ammoSchemeModel.cpp --- a/QTfrontend/ammoSchemeModel.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/ammoSchemeModel.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -577,6 +577,10 @@ QVariant AmmoSchemeModel::headerData(int section, Qt::Orientation orientation, int role) const { + Q_UNUSED(section); + Q_UNUSED(orientation); + Q_UNUSED(role); + return QVariant(); } @@ -598,6 +602,8 @@ Qt::ItemFlags AmmoSchemeModel::flags(const QModelIndex & index) const { + Q_UNUSED(index); + return Qt::ItemIsEnabled | Qt::ItemIsSelectable @@ -620,6 +626,8 @@ bool AmmoSchemeModel::insertRows(int row, int count, const QModelIndex & parent) { + Q_UNUSED(count); + beginInsertRows(parent, row, row); QList newScheme = defaultScheme; @@ -684,6 +692,10 @@ QVariant NetAmmoSchemeModel::headerData(int section, Qt::Orientation orientation, int role) const { + Q_UNUSED(section); + Q_UNUSED(orientation); + Q_UNUSED(role); + return QVariant(); } diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/bgwidget.cpp --- a/QTfrontend/bgwidget.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/bgwidget.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -103,6 +103,8 @@ void BGWidget::paintEvent(QPaintEvent *event) { + Q_UNUSED(event); + QPainter p; p.begin(this); //p.setRenderHint(QPainter::Antialiasing); diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/chatwidget.cpp --- a/QTfrontend/chatwidget.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/chatwidget.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -341,6 +341,8 @@ void HWChatWidget::chatNickSelected(int index) { + Q_UNUSED(index); + QListWidgetItem* item = chatNicks->currentItem(); if (!item) return; diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/drawmapscene.cpp --- a/QTfrontend/drawmapscene.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/drawmapscene.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -57,6 +57,8 @@ void DrawMapScene::mouseReleaseEvent(QGraphicsSceneMouseEvent * mouseEvent) { + Q_UNUSED(mouseEvent); + simplifyLast(); m_currPath = 0; @@ -73,6 +75,14 @@ } } +void DrawMapScene::clearMap() +{ + clear(); + paths.clear(); + + emit pathChanged(); +} + QByteArray DrawMapScene::encode() { QByteArray b; @@ -128,11 +138,15 @@ points.append(QPoint(px, py)); } + + emit pathChanged(); } void DrawMapScene::simplifyLast() { - QList points = paths[0]; + if(!paths.size()) return; + + QList points = paths.at(0); QPoint prevPoint = points.first(); int i = 1; diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/drawmapscene.h --- a/QTfrontend/drawmapscene.h Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/drawmapscene.h Thu Dec 23 17:47:50 2010 +0100 @@ -22,6 +22,7 @@ public slots: void undo(); + void clearMap(); void simplifyLast(); private: diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/drawmapwidget.cpp --- a/QTfrontend/drawmapwidget.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/drawmapwidget.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -1,3 +1,6 @@ +#include +#include + #include "drawmapwidget.h" DrawMapWidget::DrawMapWidget(QWidget *parent) : @@ -5,6 +8,8 @@ ui(new Ui::DrawMapWidget) { ui->setupUi(this); + + m_scene = 0; } DrawMapWidget::~DrawMapWidget() @@ -27,6 +32,7 @@ void DrawMapWidget::setScene(DrawMapScene * scene) { ui->graphicsView->setScene(scene); + m_scene = scene; } void DrawMapWidget::resizeEvent(QResizeEvent * event) @@ -36,3 +42,46 @@ if(ui->graphicsView && ui->graphicsView->scene()) ui->graphicsView->fitInView(ui->graphicsView->scene()->sceneRect(), Qt::KeepAspectRatio); } + +void DrawMapWidget::showEvent(QShowEvent * event) +{ + Q_UNUSED(event); + + resizeEvent(0); +} + +void DrawMapWidget::undo() +{ + if(m_scene) m_scene->undo(); +} + +void DrawMapWidget::clear() +{ + if(m_scene) m_scene->clearMap(); +} + +void DrawMapWidget::save(const QString & fileName) +{ + if(m_scene) + { + QFile file(fileName); + + if(!file.open(QIODevice::WriteOnly)) + QMessageBox::warning(this, tr("File error"), tr("Cannot open file '%1' for writing").arg(fileName)); + else + file.write(qCompress(m_scene->encode()).toBase64()); + } +} + +void DrawMapWidget::load(const QString & fileName) +{ + if(m_scene) + { + QFile f(fileName); + + if(!f.open(QIODevice::ReadOnly)) + QMessageBox::warning(this, tr("File error"), tr("Cannot read file '%1'").arg(fileName)); + else + m_scene->decode(qUncompress(QByteArray::fromBase64(f.readAll()))); + } +} diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/drawmapwidget.h --- a/QTfrontend/drawmapwidget.h Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/drawmapwidget.h Thu Dec 23 17:47:50 2010 +0100 @@ -15,7 +15,6 @@ { public: QGraphicsView *graphicsView; - QPushButton *pbUndo; void setupUi(QWidget *drawMapWidget) { @@ -50,12 +49,21 @@ void setScene(DrawMapScene * scene); +public slots: + void undo(); + void clear(); + void save(const QString & fileName); + void load(const QString & fileName); + protected: void changeEvent(QEvent *e); virtual void resizeEvent(QResizeEvent * event); + virtual void showEvent(QShowEvent * event); private: Ui::DrawMapWidget *ui; + + DrawMapScene * m_scene; }; #endif // DRAWMAPWIDGET_H diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/gamecfgwidget.cpp --- a/QTfrontend/gamecfgwidget.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/gamecfgwidget.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -32,7 +32,7 @@ #include "ammoSchemeModel.h" #include "proto.h" -GameCFGWidget::GameCFGWidget(QWidget* parent, bool externalControl) : +GameCFGWidget::GameCFGWidget(QWidget* parent) : QGroupBox(parent), mainLayout(this) { mainLayout.setMargin(0); @@ -43,7 +43,7 @@ IconedGroupBox *GBoxOptions = new IconedGroupBox(this); GBoxOptions->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - mainLayout.addWidget(GBoxOptions); + mainLayout.addWidget(GBoxOptions, 1, 0); QGridLayout *GBoxOptionsLayout = new QGridLayout(GBoxOptions); @@ -58,6 +58,7 @@ for (int i = 0; i < scriptList->size(); ++i) { QString script = (*scriptList)[i].remove(".lua", Qt::CaseInsensitive); QList scriptInfo; + scriptInfo.push_back(script); QFile scriptCfgFile(QString("%1/Scripts/Multiplayer/%2.cfg").arg(datadir->absolutePath()).arg(script)); if (scriptCfgFile.exists() && scriptCfgFile.open(QFile::ReadOnly)) { QString scheme; @@ -80,7 +81,7 @@ scriptInfo.push_back("locked"); scriptInfo.push_back("locked"); } - Scripts->addItem(script, scriptInfo); + Scripts->addItem(script.replace("_", " "), scriptInfo); } connect(Scripts, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptChanged(int))); @@ -244,6 +245,7 @@ { case MAPGEN_MAZE: bcfg << QString("e$maze_size %1").arg(pMapContainer->get_maze_size()).toUtf8(); + break; case MAPGEN_DRAWN: { @@ -256,6 +258,7 @@ bcfg << tmp; data.remove(0, 200); } + break; } default: ; } @@ -271,7 +274,7 @@ if (Scripts->currentIndex() > 0) { - bcfg << QString("escript Scripts/Multiplayer/%1.lua").arg(Scripts->currentText()).toUtf8(); + bcfg << QString("escript Scripts/Multiplayer/%1.lua").arg(Scripts->itemData(Scripts->currentIndex()).toList()[0].toString()).toUtf8(); } QByteArray result; @@ -351,6 +354,10 @@ Scripts->setCurrentIndex(Scripts->findText(value)); return; } + if (param == "DRAWNMAP") { + pMapContainer->setDrawnMapData(qUncompress(QByteArray::fromBase64(slValue[0].toLatin1()))); + return; + } } if (slValue.size() == 2) @@ -433,7 +440,6 @@ void GameCFGWidget::seedChanged(const QString & value) { - qDebug("GameCFGWidget::seedChanged"); emit paramChanged("SEED", QStringList(value)); } @@ -470,8 +476,8 @@ { if(index > 0) { - QString scheme = Scripts->itemData(Scripts->currentIndex()).toList()[0].toString(); - QString weapons = Scripts->itemData(Scripts->currentIndex()).toList()[1].toString(); + QString scheme = Scripts->itemData(Scripts->currentIndex()).toList()[1].toString(); + QString weapons = Scripts->itemData(Scripts->currentIndex()).toList()[2].toString(); if (scheme == "locked") { @@ -534,6 +540,5 @@ void GameCFGWidget::onDrawnMapChanged(const QByteArray & data) { - qDebug("GameCFGWidget::onDrawnMapChanged"); emit paramChanged("DRAWNMAP", QStringList(qCompress(data, 9).toBase64())); } diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/gamecfgwidget.h --- a/QTfrontend/gamecfgwidget.h Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/gamecfgwidget.h Thu Dec 23 17:47:50 2010 +0100 @@ -36,7 +36,7 @@ Q_OBJECT public: - GameCFGWidget(QWidget* parent, bool externalControl=false); + GameCFGWidget(QWidget* parent); quint32 getGameFlags() const; quint32 getInitHealth() const; QByteArray getFullConfig() const; diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/hats.cpp --- a/QTfrontend/hats.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/hats.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -77,6 +77,10 @@ QVariant HatsModel::headerData(int section, Qt::Orientation orientation, int role) const { + Q_UNUSED(section); + Q_UNUSED(orientation); + Q_UNUSED(role); + return QVariant(); } diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/hwconsts.cpp.in --- a/QTfrontend/hwconsts.cpp.in Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/hwconsts.cpp.in Thu Dec 23 17:47:50 2010 +0100 @@ -37,10 +37,10 @@ int cMaxTeams = 6; QString * cDefaultAmmoStore = new QString( - "93919294221991210322351110012010000002111101010111" - "04050405416006555465544647765766666661555101011154" - "00000000000002055000000400070040000000002000000006" - "13111103121111111231141111111111111112111111011111" + "939192942219912103223511100120100000021111010101112" + "040504054160065554655446477657666666615551010111541" + "000000000000020550000004000700400000000020000000060" + "131111031211111112311411111111111111121111110111112" ); int cAmmoNumber = cDefaultAmmoStore->size() / 4; @@ -49,40 +49,40 @@ << qMakePair(QString("Default"), *cDefaultAmmoStore) << qMakePair(QString("Crazy"), QString( // TODO: Remove Piano's unlimited uses! - "99999999999999999929999999999999992999999999099999" - "11111101111111111111111111111111111111111111011111" - "00000000000000000000000000000000000000000000000000" - "13111103121111111231141111111111111112111101011111" + "999999999999999999299999999999999929999999990999999" + "111111011111111111111111111111111111111111110111111" + "000000000000000000000000000000000000000000000000000" + "131111031211111112311411111111111111121111010111111" )) << qMakePair(QString("Pro Mode"), QString( - "90900090000000000000090000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000002055000000400070040000000002000000000" - "11111111111111111111111111111111111111111001011111" + "909000900000000000000900000000000000000000000000000" + "000000000000000000000000000000000000000000000000000" + "000000000000020550000004000700400000000020000000000" + "111111111111111111111111111111111111111110010111111" )) << qMakePair(QString("Shoppa"), QString( - "00000099000000000000000000000000000000000000000000" - "44444100442444022101121212224220000000020004000100" - "00000000000000000000000000000000000000000000000000" - "11111111111111111111111111111111111111111011011111" + "000000990000000000000000000000000000000000000000000" + "444441004424440221011212122242200000000200040001001" + "000000000000000000000000000000000000000000000000000" + "111111111111111111111111111111111111111110110111111" )) << qMakePair(QString("Clean Slate"),QString( - "10100090000100000110000000000000000000000000000010" - "04050405416006555465544647765766666661555101011154" - "00000000000000000000000000000000000000000000000000" - "13111103121111111231141111111111111112111111011111" + "101000900001000001100000000000000000000000000000100" + "040504054160065554655446477657666666615551010111541" + "000000000000000000000000000000000000000000000000000" + "131111031211111112311411111111111111121111110111111" )) << qMakePair(QString("Minefield"), QString( - "00000099000900000003000000000000000000000000000000" - "00000000000000000000000000000000000000000000000000" - "00000000000002055000000400070040000000002000000006" - "11111111111111111111111111111111111111111111011111" + "000000990009000000030000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000" + "000000000000020550000004000700400000000020000000060" + "111111111111111111111111111111111111111111110111111" )) << qMakePair(QString("Thinking with Portals"), QString( - "90000090020000000021000000000000001100000900000000" - "04050405416006555465544647765766666661555101011154" - "00000000000002055000000400070040000000002000000006" - "13111103121111111231141111111111111112111111011111" + "900000900200000000210000000000000011000009000000000" + "040504054160065554655446477657666666615551010111541" + "000000000000020550000004000700400000000020000000060" + "131111031211111112311411111111111111121111110111111" )); QColor *colors[] = { diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/hwform.cpp --- a/QTfrontend/hwform.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/hwform.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -436,7 +436,7 @@ if(id == ID_PAGE_DRAWMAP) { DrawMapScene * scene; - if(lastid = ID_PAGE_MULTIPLAYER) + if(lastid == ID_PAGE_MULTIPLAYER) scene = ui.pageMultiplayer->gameCFG->pMapContainer->getDrawMapScene(); else scene = ui.pageNetGame->pGameCFG->pMapContainer->getDrawMapScene(); @@ -445,7 +445,7 @@ } if(lastid == ID_PAGE_DRAWMAP) { - if(id = ID_PAGE_MULTIPLAYER) + if(id == ID_PAGE_MULTIPLAYER) ui.pageMultiplayer->gameCFG->pMapContainer->mapDrawingFinished(); else ui.pageNetGame->pGameCFG->pMapContainer->mapDrawingFinished(); @@ -662,6 +662,7 @@ QMessageBox::warning(0, QMessageBox::tr("Schemes"), QMessageBox::tr("Can not delete default scheme '%1'!").arg(ui.pageOptions->SchemesName->currentText())); } else { ui.pageScheme->deleteRow(); + ammoSchemeModel->Save(); } } @@ -875,7 +876,6 @@ void HWForm::NetDisconnect() { - //qDebug("NetDisconnect"); if(hwnet) { hwnet->Disconnect(); delete hwnet; @@ -898,8 +898,9 @@ { if(pnetserver) return; // we have server - let it care of all things if (hwnet) { - hwnet->deleteLater(); + HWNewNet * tmp = hwnet; hwnet = 0; + tmp->deleteLater(); QMessageBox::warning(this, QMessageBox::tr("Network"), QMessageBox::tr("Connection to server is lost")); @@ -1157,6 +1158,8 @@ void HWForm::UpdateCampaignPage(int index) { + Q_UNUSED(index); + HWTeam team(ui.pageCampaign->CBTeam->currentText()); ui.pageCampaign->CBSelect->clear(); diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/hwmap.cpp --- a/QTfrontend/hwmap.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/hwmap.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -27,13 +27,13 @@ { } -void HWMap::getImage(std::string seed, int filter, MapGenerator mapgen, int maze_size, const QByteArray & drawMapData) +void HWMap::getImage(const QString & seed, int filter, MapGenerator mapgen, int maze_size, const QByteArray & drawMapData) { m_seed = seed; templateFilter = filter; m_mapgen = mapgen; m_maze_size = maze_size; - m_drawMapData = drawMapData; + if(mapgen == MAPGEN_DRAWN) m_drawMapData = drawMapData; Start(); } @@ -60,14 +60,14 @@ void HWMap::SendToClientFirst() { - SendIPC(QString("eseed %1").arg(m_seed.c_str()).toLatin1()); - SendIPC(QString("e$template_filter %1").arg(templateFilter).toLatin1()); - SendIPC(QString("e$mapgen %1").arg(m_mapgen).toLatin1()); + SendIPC(QString("eseed %1").arg(m_seed).toUtf8()); + SendIPC(QString("e$template_filter %1").arg(templateFilter).toUtf8()); + SendIPC(QString("e$mapgen %1").arg(m_mapgen).toUtf8()); switch (m_mapgen) { case MAPGEN_MAZE: - SendIPC(QString("e$maze_size %1").arg(m_maze_size).toLatin1()); + SendIPC(QString("e$maze_size %1").arg(m_maze_size).toUtf8()); break; case MAPGEN_DRAWN: diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/hwmap.h --- a/QTfrontend/hwmap.h Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/hwmap.h Thu Dec 23 17:47:50 2010 +0100 @@ -25,14 +25,12 @@ #include "tcpBase.h" -#include - enum MapGenerator { MAPGEN_REGULAR, MAPGEN_MAZE, MAPGEN_DRAWN, - MAPGEN_LAST + MAPGEN_MAP }; class HWMap : public TCPBase @@ -42,7 +40,7 @@ public: HWMap(); virtual ~HWMap(); - void getImage(std::string seed, int templateFilter, MapGenerator mapgen, int maze_size, const QByteArray & drawMapData); + void getImage(const QString & seed, int templateFilter, MapGenerator mapgen, int maze_size, const QByteArray & drawMapData); protected: virtual QStringList setArguments(); @@ -54,7 +52,7 @@ void HHLimitReceived(int hhLimit); private: - std::string m_seed; + QString m_seed; int templateFilter; MapGenerator m_mapgen; int m_maze_size; diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/igbox.cpp --- a/QTfrontend/igbox.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/igbox.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -24,6 +24,7 @@ #include "igbox.h" IconedGroupBox::IconedGroupBox(QWidget * parent) + : QGroupBox(parent) { // Has issues with border-radius on children // setAttribute(Qt::WA_PaintOnScreen, true); @@ -56,6 +57,8 @@ void IconedGroupBox::paintEvent(QPaintEvent * event) { + Q_UNUSED(event); + QStylePainter painter(this); QStyleOptionGroupBox option; diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/itemNum.cpp --- a/QTfrontend/itemNum.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/itemNum.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -68,6 +68,8 @@ void ItemNum::paintEvent(QPaintEvent* event) { + Q_UNUSED(event); + QPainter painter(this); if (numItems==maxItems+1) { diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/mapContainer.cpp --- a/QTfrontend/mapContainer.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/mapContainer.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -30,6 +30,7 @@ #include #include #include +#include #include "hwconsts.h" #include "mapContainer.h" @@ -39,8 +40,7 @@ QWidget(parent), mainLayout(this), pMap(0), - mapgen(MAPGEN_REGULAR), - maze_size(0) + mapgen(MAPGEN_REGULAR) { hhSmall.load(":/res/hh_small.png"); hhLimit = 18; @@ -51,17 +51,23 @@ QApplication::style()->pixelMetric(QStyle::PM_LayoutRightMargin), QApplication::style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); - imageButt = new QPushButton(this); + QWidget* mapWidget = new QWidget(this); + mainLayout.addWidget(mapWidget, 0, 0, Qt::AlignHCenter); + + QGridLayout* mapLayout = new QGridLayout(mapWidget); + mapLayout->setMargin(0); + + imageButt = new QPushButton(mapWidget); imageButt->setObjectName("imageButt"); imageButt->setFixedSize(256 + 6, 128 + 6); imageButt->setFlat(true); imageButt->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);//QSizePolicy::Minimum, QSizePolicy::Minimum); - mainLayout.addWidget(imageButt, 0, 0, 1, 2); + mapLayout->addWidget(imageButt, 0, 0, 1, 2); //connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomSeed())); //connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomTheme())); connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomMap())); - chooseMap = new QComboBox(this); + chooseMap = new QComboBox(mapWidget); chooseMap->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); chooseMap->addItem( // FIXME - need real icons. Disabling until then @@ -135,29 +141,29 @@ chooseMap->insertSeparator(missionindex); // separator between missions and maps connect(chooseMap, SIGNAL(currentIndexChanged(int)), this, SLOT(mapChanged(int))); - mainLayout.addWidget(chooseMap, 1, 1); + mapLayout->addWidget(chooseMap, 1, 1); - QLabel * lblMap = new QLabel(tr("Map"), this); - mainLayout.addWidget(lblMap, 1, 0); + QLabel * lblMap = new QLabel(tr("Map"), mapWidget); + mapLayout->addWidget(lblMap, 1, 0); - lblFilter = new QLabel(tr("Filter"), this); - mainLayout.addWidget(lblFilter, 2, 0); + lblFilter = new QLabel(tr("Filter"), mapWidget); + mapLayout->addWidget(lblFilter, 2, 0); - CB_TemplateFilter = new QComboBox(this); + CB_TemplateFilter = new QComboBox(mapWidget); CB_TemplateFilter->addItem(tr("All"), 0); CB_TemplateFilter->addItem(tr("Small"), 1); CB_TemplateFilter->addItem(tr("Medium"), 2); CB_TemplateFilter->addItem(tr("Large"), 3); CB_TemplateFilter->addItem(tr("Cavern"), 4); CB_TemplateFilter->addItem(tr("Wacky"), 5); - mainLayout.addWidget(CB_TemplateFilter, 2, 1); + mapLayout->addWidget(CB_TemplateFilter, 2, 1); connect(CB_TemplateFilter, SIGNAL(currentIndexChanged(int)), this, SLOT(templateFilterChanged(int))); - maze_size_label = new QLabel(tr("Type"), this); + maze_size_label = new QLabel(tr("Type"), mapWidget); mainLayout.addWidget(maze_size_label, 2, 0); maze_size_label->hide(); - maze_size_selection = new QComboBox(this); + maze_size_selection = new QComboBox(mapWidget); maze_size_selection->addItem(tr("Small tunnels"), 0); maze_size_selection->addItem(tr("Medium tunnels"), 1); maze_size_selection->addItem(tr("Large tunnels"), 2); @@ -165,23 +171,23 @@ maze_size_selection->addItem(tr("Medium floating islands"), 4); maze_size_selection->addItem(tr("Large floating islands"), 5); maze_size_selection->setCurrentIndex(1); - maze_size = 1; - mainLayout.addWidget(maze_size_selection, 2, 1); + + mapLayout->addWidget(maze_size_selection, 2, 1); maze_size_selection->hide(); connect(maze_size_selection, SIGNAL(currentIndexChanged(int)), this, SLOT(setMaze_size(int))); - gbThemes = new IconedGroupBox(this); + gbThemes = new IconedGroupBox(mapWidget); gbThemes->setTitleTextPadding(60); gbThemes->setContentTopPadding(6); gbThemes->setTitle(tr("Themes")); //gbThemes->setStyleSheet("padding: 0px"); // doesn't work - stylesheet is set with icon - mainLayout.addWidget(gbThemes, 0, 2, 3, 1); + mapLayout->addWidget(gbThemes, 0, 2, 3, 1); QVBoxLayout * gbTLayout = new QVBoxLayout(gbThemes); gbTLayout->setContentsMargins(0, 0, 0 ,0); gbTLayout->setSpacing(0); - lwThemes = new QListWidget(this); + lwThemes = new QListWidget(mapWidget); lwThemes->setMinimumHeight(30); lwThemes->setFixedWidth(140); for (int i = 0; i < Themes->size(); ++i) { @@ -210,13 +216,26 @@ gbTLayout->addWidget(lwThemes); lwThemes->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); - QLabel* seedLabel = new QLabel(tr("Seed"), this); - mainLayout.addWidget(seedLabel, 3, 0); - seedEdit = new QLineEdit(this); - mainLayout.addWidget(seedEdit, 3, 1, 1, 2); - connect(seedEdit, SIGNAL(textChanged(const QString&)), this, SLOT(seedEdited(const QString&))); + mapLayout->setSizeConstraint(QLayout::SetFixedSize); + + QWidget* seedWidget = new QWidget(this); + mainLayout.addWidget(seedWidget, 1, 0); + + QGridLayout* seedLayout = new QGridLayout(seedWidget); + seedLayout->setMargin(0); - mainLayout.setSizeConstraint(QLayout::SetFixedSize);//SetMinimumSize + QLabel* seedLabel = new QLabel(tr("Seed"), seedWidget); + seedLayout->addWidget(seedLabel, 3, 0); + seedEdit = new QLineEdit(seedWidget); + seedEdit->setMaxLength(54); + connect(seedEdit, SIGNAL(returnPressed()), this, SLOT(seedEdited())); + seedLayout->addWidget(seedEdit, 3, 1); + seedLayout->setColumnStretch(1, 5); + seedSet = new QPushButton(seedWidget); + seedSet->setText(QPushButton::tr("Set")); + connect(seedSet, SIGNAL(clicked()), this, SLOT(seedEdited())); + seedLayout->setColumnStretch(2, 1); + seedLayout->addWidget(seedSet, 3, 2); setRandomSeed(); setRandomTheme(); @@ -239,7 +258,7 @@ p.drawPixmap(QPoint(0, 0), px); addInfoToPreview(pxres); - chooseMap->setCurrentIndex(mapgen); + //chooseMap->setCurrentIndex(mapgen); pMap = 0; } @@ -253,7 +272,7 @@ switch(index) { case MAPGEN_REGULAR: mapgen = MAPGEN_REGULAR; - changeImage(); + updatePreview(); gbThemes->show(); lblFilter->show(); CB_TemplateFilter->show(); @@ -265,7 +284,7 @@ break; case MAPGEN_MAZE: mapgen = MAPGEN_MAZE; - changeImage(); + updatePreview(); gbThemes->show(); lblFilter->hide(); CB_TemplateFilter->hide(); @@ -277,7 +296,7 @@ break; case MAPGEN_DRAWN: mapgen = MAPGEN_DRAWN; - changeImage(); + updatePreview(); gbThemes->show(); lblFilter->hide(); CB_TemplateFilter->hide(); @@ -288,7 +307,7 @@ emit themeChanged(chooseMap->itemData(index).toList()[1].toString()); break; default: - loadMap(index); + updatePreview(); gbThemes->hide(); lblFilter->hide(); CB_TemplateFilter->hide(); @@ -298,19 +317,6 @@ } } -void HWMapContainer::loadMap(int index) -{ - QPixmap mapImage; - if(!mapImage.load(datadir->absolutePath() + "/Maps/" + chooseMap->itemData(index).toList()[0].toString() + "/preview.png")) { - changeImage(); - chooseMap->setCurrentIndex(0); - return; - } - - hhLimit = chooseMap->itemData(index).toList()[2].toInt(); - addInfoToPreview(mapImage); -} - // Should this add text to identify map size? void HWMapContainer::addInfoToPreview(QPixmap image) { @@ -331,7 +337,7 @@ imageButt->setIconSize(image.size()); } -void HWMapContainer::changeImage() +void HWMapContainer::askForGeneratedPreview() { if (pMap) { @@ -343,7 +349,12 @@ pMap = new HWMap(); connect(pMap, SIGNAL(ImageReceived(const QImage)), this, SLOT(setImage(const QImage))); connect(pMap, SIGNAL(HHLimitReceived(int)), this, SLOT(setHHLimit(int))); - pMap->getImage(m_seed.toStdString(), getTemplateFilter(), mapgen, maze_size, getDrawnMapData()); + pMap->getImage(m_seed, + getTemplateFilter(), + get_mapgen(), + get_maze_size(), + getDrawnMapData() + ); } void HWMapContainer::themeSelected(int currentRow) @@ -370,7 +381,7 @@ QString HWMapContainer::getCurrentMap() const { - if(chooseMap->currentIndex() <= 2) return QString(); + if(chooseMap->currentIndex() < MAPGEN_MAP) return QString(); return chooseMap->itemData(chooseMap->currentIndex()).toList()[0].toString(); } @@ -407,6 +418,7 @@ void HWMapContainer::resizeEvent ( QResizeEvent * event ) { + Q_UNUSED(event); //imageButt->setIconSize(imageButt->size()); } @@ -415,18 +427,12 @@ m_seed = seed; if (seed != seedEdit->text()) seedEdit->setText(seed); - if (chooseMap->currentIndex() < MAPGEN_LAST) - changeImage(); + if (chooseMap->currentIndex() < MAPGEN_MAP) + updatePreview(); } void HWMapContainer::setMap(const QString & map) { - if(map == "+rnd+" || map == "+maze+" || map == "+drawn+") - { - changeImage(); - return; - } - int id = 0; for(int i = 0; i < chooseMap->count(); i++) if(!chooseMap->itemData(i).isNull() && chooseMap->itemData(i).toList()[0].toString() == map) @@ -443,7 +449,7 @@ pMap = 0; } chooseMap->setCurrentIndex(id); - loadMap(id); + updatePreview(); } } @@ -453,7 +459,7 @@ if(items.size()) lwThemes->setCurrentItem(items.at(0)); } -#include + void HWMapContainer::setRandomMap() { setRandomSeed(); @@ -467,7 +473,7 @@ emit drawMapRequested(); break; default: - if(chooseMap->currentIndex() < numMissions + 4) + if(chooseMap->currentIndex() <= numMissions + MAPGEN_MAP + 1) setRandomMission(); else setRandomStatic(); @@ -477,13 +483,16 @@ void HWMapContainer::setRandomStatic() { - chooseMap->setCurrentIndex(4 + numMissions + rand() % (chooseMap->count() - 4 - numMissions)); + int i = MAPGEN_MAP + 3 + numMissions + rand() % (chooseMap->count() - MAPGEN_MAP - 3 - numMissions); + chooseMap->setCurrentIndex(i); setRandomSeed(); } void HWMapContainer::setRandomMission() { - chooseMap->setCurrentIndex(3 + rand() % numMissions); + int i = MAPGEN_MAP + 2 + rand() % numMissions; + qDebug() << i << MAPGEN_MAP << numMissions; + chooseMap->setCurrentIndex(i); setRandomSeed(); } @@ -492,8 +501,8 @@ m_seed = QUuid::createUuid().toString(); seedEdit->setText(m_seed); emit seedChanged(m_seed); - if (chooseMap->currentIndex() < MAPGEN_LAST) - changeImage(); + if (chooseMap->currentIndex() < MAPGEN_MAP) + updatePreview(); } void HWMapContainer::setRandomTheme() @@ -511,7 +520,7 @@ void HWMapContainer::templateFilterChanged(int filter) { emit newTemplateFilter(filter); - changeImage(); + updatePreview(); } MapGenerator HWMapContainer::get_mapgen(void) const @@ -521,22 +530,28 @@ int HWMapContainer::get_maze_size(void) const { - return maze_size; + return maze_size_selection->currentIndex(); } void HWMapContainer::setMaze_size(int size) { - maze_size = size; maze_size_selection->setCurrentIndex(size); emit maze_sizeChanged(size); - changeImage(); + updatePreview(); } void HWMapContainer::setMapgen(MapGenerator m) { mapgen = m; + chooseMap->setCurrentIndex(m); emit mapgenChanged(m); - changeImage(); + updatePreview(); +} + +void HWMapContainer::setDrawnMapData(const QByteArray & ar) +{ + drawMapScene.decode(ar); + updatePreview(); } QByteArray HWMapContainer::getDrawnMapData() @@ -544,14 +559,14 @@ return drawMapScene.encode(); } -void HWMapContainer::seedEdited(const QString & seed) +void HWMapContainer::seedEdited() { - if (seed.isEmpty() || seed.size() > 54) + if (seedEdit->text().isEmpty()) seedEdit->setText(m_seed); else { - setSeed(seed); - emit seedChanged(seed); + setSeed(seedEdit->text()); + emit seedChanged(seedEdit->text()); } } @@ -564,5 +579,33 @@ { emit drawnMapChanged(getDrawnMapData()); - changeImage(); -} \ No newline at end of file + updatePreview(); +} + +void HWMapContainer::updatePreview() +{ + int curIndex = chooseMap->currentIndex(); + + switch(curIndex) + { + case MAPGEN_REGULAR: + askForGeneratedPreview(); + break; + case MAPGEN_MAZE: + askForGeneratedPreview(); + break; + case MAPGEN_DRAWN: + askForGeneratedPreview(); + break; + default: + QPixmap mapImage; + qDebug() << "Map data" << curIndex << chooseMap->currentText() << chooseMap->itemData(curIndex); + if(!mapImage.load(datadir->absolutePath() + "/Maps/" + chooseMap->itemData(curIndex).toList()[0].toString() + "/preview.png")) { + imageButt->setIcon(QIcon()); + return; + } + + hhLimit = chooseMap->itemData(curIndex).toList()[2].toInt(); + addInfoToPreview(mapImage); + } +} diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/mapContainer.h --- a/QTfrontend/mapContainer.h Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/mapContainer.h Thu Dec 23 17:47:50 2010 +0100 @@ -55,16 +55,17 @@ bool getCurrentIsMission() const; QByteArray getDrawnMapData(); DrawMapScene * getDrawMapScene(); + void mapDrawingFinished(); public slots: - void changeImage(); - void mapDrawingFinished(); + void askForGeneratedPreview(); void setSeed(const QString & seed); void setMap(const QString & map); void setTheme(const QString & theme); void setTemplateFilter(int); void setMapgen(MapGenerator m); void setMaze_size(int size); + void setDrawnMapData(const QByteArray & ar); signals: void seedChanged(const QString & seed); @@ -88,7 +89,7 @@ void themeSelected(int currentRow); void addInfoToPreview(QPixmap image); void templateFilterChanged(int filter); - void seedEdited(const QString & seed); + void seedEdited(); protected: virtual void resizeEvent ( QResizeEvent * event ); @@ -102,6 +103,7 @@ HWMap* pMap; QString m_seed; QLineEdit* seedEdit; + QPushButton* seedSet; int hhLimit; int templateFilter; QPixmap hhSmall; @@ -111,10 +113,9 @@ QComboBox *maze_size_selection; MapGenerator mapgen; int numMissions; - int maze_size; DrawMapScene drawMapScene; - void loadMap(int index); + void updatePreview(); }; #endif // _HWMAP_CONTAINER_INCLUDED diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/netregister.cpp --- a/QTfrontend/netregister.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/netregister.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -21,7 +21,8 @@ HWNetRegisterServer::HWNetRegisterServer(QObject *parent, const QString & descr, quint16 port) : QObject(parent) { - + Q_UNUSED(descr); + Q_UNUSED(port); } void HWNetRegisterServer::unregister() diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/newnetclient.cpp diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/pages.cpp --- a/QTfrontend/pages.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/pages.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -40,6 +40,7 @@ #include #include #include +#include #include "ammoSchemeModel.h" #include "pages.h" @@ -563,7 +564,6 @@ SchemeDelete->setIconSize(pmDelete.size()); SchemeDelete->setIcon(pmDelete); SchemeDelete->setMaximumWidth(pmDelete.width() + 6); - SchemeDelete->setEnabled(false); WeaponsLayout->addWidget(SchemeDelete, 1, 4); QLabel* WeaponLabel = new QLabel(groupWeapons); @@ -2018,8 +2018,34 @@ { QGridLayout * pageLayout = new QGridLayout(this); - BtnBack = addButton(":/res/Exit.png", pageLayout, 1, 0, true); + QPushButton * pbUndo = addButton(tr("Undo"), pageLayout, 0, 0); + QPushButton * pbClear = addButton(tr("Clear"), pageLayout, 1, 0); + QPushButton * pbLoad = addButton(tr("Load"), pageLayout, 2, 0); + QPushButton * pbSave = addButton(tr("Save"), pageLayout, 3, 0); + + BtnBack = addButton(":/res/Exit.png", pageLayout, 5, 0, true); drawMapWidget = new DrawMapWidget(this); - pageLayout->addWidget(drawMapWidget, 0, 0, 1, 2); + pageLayout->addWidget(drawMapWidget, 0, 1, 5, 1); + + connect(pbUndo, SIGNAL(clicked()), drawMapWidget, SLOT(undo())); + connect(pbClear, SIGNAL(clicked()), drawMapWidget, SLOT(clear())); + connect(pbLoad, SIGNAL(clicked()), this, SLOT(load())); + connect(pbSave, SIGNAL(clicked()), this, SLOT(save())); } + +void PageDrawMap::load() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Load drawn map"), ".", tr("Drawn Maps (*.hwmap);;All files (*.*)")); + + if(!fileName.isEmpty()) + drawMapWidget->load(fileName); +} + +void PageDrawMap::save() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save drawn map"), ".", tr("Drawn Maps (*.hwmap);;All files (*.*)")); + + if(!fileName.isEmpty()) + drawMapWidget->save(fileName); +} diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/pages.h --- a/QTfrontend/pages.h Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/pages.h Thu Dec 23 17:47:50 2010 +0100 @@ -71,6 +71,8 @@ protected: AbstractPage(QWidget* parent = 0) { + Q_UNUSED(parent); + font14 = new QFont("MS Shell Dlg", 14); setFocusPolicy(Qt::StrongFocus); } @@ -585,6 +587,10 @@ QPushButton * BtnBack; DrawMapWidget * drawMapWidget; + +private slots: + void load(); + void save(); }; #endif // PAGES_H diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/statsPage.cpp --- a/QTfrontend/statsPage.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/statsPage.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -31,6 +31,8 @@ void FitGraphicsView::resizeEvent(QResizeEvent * event) { + Q_UNUSED(event); + fitInView(sceneRect()); } diff -r 467ab0685890 -r 5ef5415c4ee1 QTfrontend/togglebutton.cpp --- a/QTfrontend/togglebutton.cpp Tue Dec 14 22:32:47 2010 +0100 +++ b/QTfrontend/togglebutton.cpp Thu Dec 23 17:47:50 2010 +0100 @@ -19,6 +19,7 @@ #include "togglebutton.h" ToggleButtonWidget::ToggleButtonWidget(QWidget * parent, QString img) + : QPushButton(parent) { setCheckable(true); diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/Actions.hs --- a/gameServer/Actions.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/Actions.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,134 +1,171 @@ -{-# LANGUAGE OverloadedStrings #-} module Actions where -import Control.Concurrent +import Control.Concurrent.STM import Control.Concurrent.Chan +import Data.IntMap import qualified Data.IntSet as IntSet -import qualified Data.Set as Set import qualified Data.Sequence as Seq import System.Log.Logger import Control.Monad import Data.Time import Data.Maybe -import Control.Monad.Reader -import Control.Monad.State.Strict -import qualified Data.ByteString.Char8 as B ----------------------------- import CoreTypes import Utils -import ClientIO -import ServerState data Action = - AnswerClients ![ClientChan] ![B.ByteString] + AnswerThisClient [String] + | AnswerAll [String] + | AnswerAllOthers [String] + | AnswerThisRoom [String] + | AnswerOthersInRoom [String] + | AnswerSameClan [String] + | AnswerLobby [String] | SendServerMessage | SendServerVars - | MoveToRoom RoomIndex - | MoveToLobby B.ByteString - | RemoveTeam B.ByteString + | RoomAddThisClient Int -- roomID + | RoomRemoveThisClient String + | RemoveTeam String | RemoveRoom | UnreadyRoomClients - | JoinLobby - | ProtocolError B.ByteString - | Warning B.ByteString - | ByeClient B.ByteString - | KickClient ClientIndex - | KickRoomClient ClientIndex - | BanClient B.ByteString -- nick - | RemoveClientTeams ClientIndex + | MoveToLobby + | ProtocolError String + | Warning String + | ByeClient String + | KickClient Int -- clID + | KickRoomClient Int -- clID + | BanClient String -- nick + | RemoveClientTeams Int -- clID | ModifyClient (ClientInfo -> ClientInfo) - | ModifyClient2 ClientIndex (ClientInfo -> ClientInfo) + | ModifyClient2 Int (ClientInfo -> ClientInfo) | ModifyRoom (RoomInfo -> RoomInfo) | ModifyServerInfo (ServerInfo -> ServerInfo) - | AddRoom B.ByteString B.ByteString + | AddRoom String String | CheckRegistered | ClearAccountsCache | ProcessAccountInfo AccountInfo | Dump | AddClient ClientInfo - | DeleteClient ClientIndex | PingAll | StatsAction -type CmdHandler = [B.ByteString] -> Reader (ClientIndex, IRnC) [Action] +type CmdHandler = Int -> Clients -> Rooms -> [String] -> [Action] + +replaceID a (b, c, d, e) = (a, c, d, e) + +processAction :: (Int, ServerInfo, Clients, Rooms) -> Action -> IO (Int, ServerInfo, Clients, Rooms) + + +processAction (clID, serverInfo, clients, rooms) (AnswerThisClient msg) = do + writeChan (sendChan $ clients ! clID) msg + return (clID, serverInfo, clients, rooms) -processAction :: Action -> StateT ServerState IO () +processAction (clID, serverInfo, clients, rooms) (AnswerAll msg) = do + mapM_ (\cl -> writeChan (sendChan cl) msg) (elems clients) + return (clID, serverInfo, clients, rooms) + + +processAction (clID, serverInfo, clients, rooms) (AnswerAllOthers msg) = do + mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) $ + Prelude.filter (\id' -> (id' /= clID) && logonPassed (clients ! id')) (keys clients) + return (clID, serverInfo, clients, rooms) + + +processAction (clID, serverInfo, clients, rooms) (AnswerThisRoom msg) = do + mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) roomClients + return (clID, serverInfo, clients, rooms) + where + roomClients = IntSet.elems $ playersIDs room + room = rooms ! rID + rID = roomID client + client = clients ! clID -processAction (AnswerClients chans msg) = do - liftIO $ map (flip seq ()) chans `seq` map (flip seq ()) msg `seq` mapM_ (flip writeChan msg) chans +processAction (clID, serverInfo, clients, rooms) (AnswerOthersInRoom msg) = do + mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) $ Prelude.filter (/= clID) roomClients + return (clID, serverInfo, clients, rooms) + where + roomClients = IntSet.elems $ playersIDs room + room = rooms ! rID + rID = roomID client + client = clients ! clID + + +processAction (clID, serverInfo, clients, rooms) (AnswerLobby msg) = do + mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) roomClients + return (clID, serverInfo, clients, rooms) + where + roomClients = IntSet.elems $ playersIDs room + room = rooms ! 0 -processAction SendServerMessage = do - chan <- client's sendChan - protonum <- client's clientProto - si <- liftM serverInfo get - let message = if protonum < latestReleaseVersion si then +processAction (clID, serverInfo, clients, rooms) (AnswerSameClan msg) = do + mapM_ (\cl -> writeChan (sendChan cl) msg) sameClanOrSpec + return (clID, serverInfo, clients, rooms) + where + otherRoomClients = Prelude.map ((!) clients) $ IntSet.elems $ clID `IntSet.delete` (playersIDs room) + sameClanOrSpec = if teamsInGame client > 0 then sameClanClients else spectators + spectators = Prelude.filter (\cl -> teamsInGame cl == 0) otherRoomClients + sameClanClients = Prelude.filter (\cl -> teamsInGame cl > 0 && clientClan cl == thisClan) otherRoomClients + thisClan = clientClan client + room = rooms ! rID + rID = roomID client + client = clients ! clID + + +processAction (clID, serverInfo, clients, rooms) SendServerMessage = do + writeChan (sendChan $ clients ! clID) ["SERVER_MESSAGE", message serverInfo] + return (clID, serverInfo, clients, rooms) + where + client = clients ! clID + message si = if clientProto client < latestReleaseVersion si then serverMessageForOldVersions si else serverMessage si - processAction $ AnswerClients [chan] ["SERVER_MESSAGE", message] -{- -processAction (clID, serverInfo, rnc) SendServerVars = do +processAction (clID, serverInfo, clients, rooms) SendServerVars = do writeChan (sendChan $ clients ! clID) ("SERVER_VARS" : vars) - return (clID, serverInfo, rnc) + return (clID, serverInfo, clients, rooms) where client = clients ! clID vars = [ - "MOTD_NEW", serverMessage serverInfo, - "MOTD_OLD", serverMessageForOldVersions serverInfo, + "MOTD_NEW", serverMessage serverInfo, + "MOTD_OLD", serverMessageForOldVersions serverInfo, "LATEST_PROTO", show $ latestReleaseVersion serverInfo ] --} +processAction (clID, serverInfo, clients, rooms) (ProtocolError msg) = do + writeChan (sendChan $ clients ! clID) ["ERROR", msg] + return (clID, serverInfo, clients, rooms) -processAction (ProtocolError msg) = do - chan <- client's sendChan - processAction $ AnswerClients [chan] ["ERROR", msg] + +processAction (clID, serverInfo, clients, rooms) (Warning msg) = do + writeChan (sendChan $ clients ! clID) ["WARNING", msg] + return (clID, serverInfo, clients, rooms) -processAction (Warning msg) = do - chan <- client's sendChan - processAction $ AnswerClients [chan] ["WARNING", msg] - -processAction (ByeClient msg) = do - (Just ci) <- gets clientIndex - rnc <- gets roomsClients - ri <- clientRoomA - - chan <- client's sendChan - ready <- client's isReady +processAction (clID, serverInfo, clients, rooms) (ByeClient msg) = do + infoM "Clients" (show (clientUID client) ++ " quits: " ++ msg) + (_, _, newClients, newRooms) <- + if roomID client /= 0 then + processAction (clID, serverInfo, clients, rooms) $ RoomRemoveThisClient "quit" + else + return (clID, serverInfo, clients, rooms) - when (ri /= lobbyId) $ do - processAction $ MoveToLobby ("quit: " `B.append` msg) - liftIO $ modifyRoom rnc (\r -> r{ - --playersIDs = IntSet.delete ci (playersIDs r) - playersIn = (playersIn r) - 1, - readyPlayers = if ready then readyPlayers r - 1 else readyPlayers r - }) ri - return () - - liftIO $ do - infoM "Clients" (show ci ++ " quits: " ++ (B.unpack msg)) - - --mapM_ (processAction (ci, serverInfo, rnc)) $ answerOthersQuit ++ answerInformRoom - - processAction $ AnswerClients [chan] ["BYE", msg] - - s <- get - put $! s{removedClients = ci `Set.insert` removedClients s} - -processAction (DeleteClient ci) = do - rnc <- gets roomsClients - liftIO $ removeClient rnc ci - - s <- get - put $! s{removedClients = ci `Set.delete` removedClients s} - -{- + mapM_ (processAction (clID, serverInfo, newClients, newRooms)) $ answerOthersQuit ++ answerInformRoom + writeChan (sendChan $ clients ! clID) ["BYE", msg] + return ( + 0, + serverInfo, + delete clID newClients, + adjust (\r -> r{ + playersIDs = IntSet.delete clID (playersIDs r), + playersIn = (playersIn r) - 1, + readyPlayers = if isReady client then readyPlayers r - 1 else readyPlayers r + }) (roomID $ newClients ! clID) newRooms + ) where client = clients ! clID clientNick = nick client @@ -147,57 +184,46 @@ else [AnswerAll ["LOBBY:LEFT", clientNick]] else - [] --} + [] + + +processAction (clID, serverInfo, clients, rooms) (ModifyClient func) = + return (clID, serverInfo, adjust func clID clients, rooms) + -processAction (ModifyClient f) = do - (Just ci) <- gets clientIndex - rnc <- gets roomsClients - liftIO $ modifyClient rnc f ci - return () +processAction (clID, serverInfo, clients, rooms) (ModifyClient2 cl2ID func) = + return (clID, serverInfo, adjust func cl2ID clients, rooms) + -processAction (ModifyClient2 ci f) = do - rnc <- gets roomsClients - liftIO $ modifyClient rnc f ci - return () +processAction (clID, serverInfo, clients, rooms) (ModifyRoom func) = + return (clID, serverInfo, clients, adjust func rID rooms) + where + rID = roomID $ clients ! clID -processAction (ModifyRoom f) = do - rnc <- gets roomsClients - ri <- clientRoomA - liftIO $ modifyRoom rnc f ri - return () +processAction (clID, serverInfo, clients, rooms) (ModifyServerInfo func) = + return (clID, func serverInfo, clients, rooms) -{- - -processAction (clID, serverInfo, rnc) (ModifyServerInfo func) = - return (clID, func serverInfo, rnc) - --} -processAction (MoveToRoom ri) = do - (Just ci) <- gets clientIndex - rnc <- gets roomsClients - liftIO $ do - modifyClient rnc (\cl -> cl{teamsInGame = 0}) ci - modifyRoom rnc (\r -> r{playersIn = (playersIn r) + 1}) ri - - liftIO $ moveClientToRoom rnc ri ci - - chans <- liftM (map sendChan) $ roomClientsS ri - clNick <- client's nick +processAction (clID, serverInfo, clients, rooms) (RoomAddThisClient rID) = + processAction ( + clID, + serverInfo, + adjust (\cl -> cl{roomID = rID, teamsInGame = if rID == 0 then teamsInGame cl else 0}) clID clients, + adjust (\r -> r{playersIDs = IntSet.insert clID (playersIDs r), playersIn = (playersIn r) + 1}) rID $ + adjust (\r -> r{playersIDs = IntSet.delete clID (playersIDs r)}) 0 rooms + ) joinMsg + where + client = clients ! clID + joinMsg = if rID == 0 then + AnswerAllOthers ["LOBBY:JOINED", nick client] + else + AnswerThisRoom ["JOINED", nick client] - processAction $ AnswerClients chans ["JOINED", clNick] -processAction (MoveToLobby msg) = do - (Just ci) <- gets clientIndex - --ri <- clientRoomA - rnc <- gets roomsClients - - liftIO $ moveClientToLobby rnc ci - -{- +processAction (clID, serverInfo, clients, rooms) (RoomRemoveThisClient msg) = do (_, _, newClients, newRooms) <- + if roomID client /= 0 then if isMaster client then if (gameinprogress room) && (playersIn room > 1) then (changeMaster >>= (\state -> foldM processAction state @@ -205,15 +231,16 @@ AnswerOthersInRoom ["WARNING", "Admin left the room"], RemoveClientTeams clID])) else -- not in game - processAction (clID, serverInfo, rnc) RemoveRoom + processAction (clID, serverInfo, clients, rooms) RemoveRoom else -- not master foldM processAction - (clID, serverInfo, rnc) + (clID, serverInfo, clients, rooms) [AnswerOthersInRoom ["LEFT", nick client, msg], RemoveClientTeams clID] - - + else -- in lobby + return (clID, serverInfo, clients, rooms) + return ( clID, serverInfo, @@ -232,7 +259,7 @@ } insertClientToRoom r = r{playersIDs = IntSet.insert clID (playersIDs r)} changeMaster = do - processAction (newMasterId, serverInfo, rnc) $ AnswerThisClient ["ROOM_CONTROL_ACCESS", "1"] + processAction (newMasterId, serverInfo, clients, rooms) $ AnswerThisClient ["ROOM_CONTROL_ACCESS", "1"] return ( clID, serverInfo, @@ -243,35 +270,34 @@ otherPlayersSet = IntSet.delete clID (playersIDs room) newMasterId = IntSet.findMin otherPlayersSet newMasterClient = clients ! newMasterId --} + -processAction (AddRoom roomName roomPassword) = do - Just clId <- gets clientIndex - rnc <- gets roomsClients - proto <- liftIO $ client'sM rnc clientProto clId - +processAction (clID, serverInfo, clients, rooms) (AddRoom roomName roomPassword) = do + let newServerInfo = serverInfo {nextRoomID = newID} let room = newRoom{ - masterID = clId, + roomUID = newID, + masterID = clID, name = roomName, password = roomPassword, - roomProto = proto + roomProto = (clientProto client) } - rId <- liftIO $ addRoom rnc room - - processAction $ MoveToRoom rId - - chans <- liftM (map sendChan) $! roomClientsS lobbyId + processAction (clID, serverInfo, clients, rooms) $ AnswerLobby ["ROOM", "ADD", roomName] - mapM_ processAction [ - AnswerClients chans ["ROOM", "ADD", roomName] - , ModifyClient (\cl -> cl{isMaster = True}) - ] + processAction ( + clID, + newServerInfo, + adjust (\cl -> cl{isMaster = True}) clID clients, + insert newID room rooms + ) $ RoomAddThisClient newID + where + newID = (nextRoomID serverInfo) - 1 + client = clients ! clID -{- -processAction (clID, serverInfo, rnc) (RemoveRoom) = do - processAction (clID, serverInfo, rnc) $ AnswerLobby ["ROOM", "DEL", name room] - processAction (clID, serverInfo, rnc) $ AnswerOthersInRoom ["ROOMABANDONED", name room] + +processAction (clID, serverInfo, clients, rooms) (RemoveRoom) = do + processAction (clID, serverInfo, clients, rooms) $ AnswerLobby ["ROOM", "DEL", name room] + processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["ROOMABANDONED", name room] return (clID, serverInfo, Data.IntMap.map (\cl -> if roomID cl == rID then cl{roomID = 0, isMaster = False, isReady = False, teamsInGame = undefined} else cl) clients, @@ -282,163 +308,139 @@ rID = roomID client client = clients ! clID --} -processAction (UnreadyRoomClients) = do - rnc <- gets roomsClients - ri <- clientRoomA - roomPlayers <- roomClientsS ri - roomClIDs <- liftIO $ roomClientsIndicesM rnc ri - processAction $ AnswerClients (map sendChan roomPlayers) ("NOT_READY" : map nick roomPlayers) - liftIO $ mapM_ (modifyClient rnc (\cl -> cl{isReady = False})) roomClIDs - processAction $ ModifyRoom (\r -> r{readyPlayers = 0}) + +processAction (clID, serverInfo, clients, rooms) (UnreadyRoomClients) = do + processAction (clID, serverInfo, clients, rooms) $ AnswerThisRoom ("NOT_READY" : roomPlayers) + return (clID, + serverInfo, + Data.IntMap.map (\cl -> if roomID cl == rID then cl{isReady = False} else cl) clients, + adjust (\r -> r{readyPlayers = 0}) rID rooms) + where + room = rooms ! rID + rID = roomID client + client = clients ! clID + roomPlayers = Prelude.map (nick . (clients !)) roomPlayersIDs + roomPlayersIDs = IntSet.elems $ playersIDs room -processAction (RemoveTeam teamName) = do - rnc <- gets roomsClients - cl <- client's id - ri <- clientRoomA - inGame <- liftIO $ room'sM rnc gameinprogress ri - chans <- liftM (map sendChan . filter (/= cl)) $ roomClientsS ri - if inGame then - mapM_ processAction [ - AnswerClients chans ["REMOVE_TEAM", teamName], - ModifyRoom (\r -> r{teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r}) - ] +processAction (clID, serverInfo, clients, rooms) (RemoveTeam teamName) = do + newRooms <- if not $ gameinprogress room then + do + processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["REMOVE_TEAM", teamName] + return $ + adjust (\r -> r{teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r}) rID rooms else - mapM_ processAction [ - AnswerClients chans ["EM", rmTeamMsg], - ModifyRoom (\r -> r{ - teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r, - leftTeams = teamName : leftTeams r, - roundMsgs = roundMsgs r Seq.|> rmTeamMsg - }) - ] + do + processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["EM", rmTeamMsg] + return $ + adjust (\r -> r{ + teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r, + leftTeams = teamName : leftTeams r, + roundMsgs = roundMsgs r Seq.|> rmTeamMsg + }) rID rooms + return (clID, serverInfo, clients, newRooms) where - rmTeamMsg = toEngineMsg $ (B.singleton 'F') `B.append` teamName + room = rooms ! rID + rID = roomID client + client = clients ! clID + rmTeamMsg = toEngineMsg $ 'F' : teamName -processAction CheckRegistered = do - (Just ci) <- gets clientIndex - n <- client's nick - h <- client's host - db <- gets (dbQueries . serverInfo) - liftIO $ writeChan db $ CheckAccount ci n h - return () -{- -processAction (clID, serverInfo, rnc) (ClearAccountsCache) = do - writeChan (dbQueries serverInfo) ClearCache - return (clID, serverInfo, rnc) +processAction (clID, serverInfo, clients, rooms) (CheckRegistered) = do + writeChan (dbQueries serverInfo) $ CheckAccount (clientUID client) (nick client) (host client) + return (clID, serverInfo, clients, rooms) where client = clients ! clID -processAction (clID, serverInfo, rnc) (Dump) = do +processAction (clID, serverInfo, clients, rooms) (ClearAccountsCache) = do + writeChan (dbQueries serverInfo) ClearCache + return (clID, serverInfo, clients, rooms) + where + client = clients ! clID + + +processAction (clID, serverInfo, clients, rooms) (Dump) = do writeChan (sendChan $ clients ! clID) ["DUMP", show serverInfo, showTree clients, showTree rooms] - return (clID, serverInfo, rnc) --} + return (clID, serverInfo, clients, rooms) -processAction (ProcessAccountInfo info) = + +processAction (clID, serverInfo, clients, rooms) (ProcessAccountInfo info) = case info of HasAccount passwd isAdmin -> do - chan <- client's sendChan - processAction $ AnswerClients [chan] ["ASKPASSWORD"] + infoM "Clients" $ show clID ++ " has account" + writeChan (sendChan $ clients ! clID) ["ASKPASSWORD"] + return (clID, serverInfo, adjust (\cl -> cl{webPassword = passwd, isAdministrator = isAdmin}) clID clients, rooms) Guest -> do - processAction JoinLobby + infoM "Clients" $ show clID ++ " is guest" + processAction (clID, serverInfo, adjust (\cl -> cl{logonPassed = True}) clID clients, rooms) MoveToLobby Admin -> do - mapM processAction [ModifyClient (\cl -> cl{isAdministrator = True}), JoinLobby] - chan <- client's sendChan - processAction $ AnswerClients [chan] ["ADMIN_ACCESS"] + infoM "Clients" $ show clID ++ " is admin" + foldM processAction (clID, serverInfo, adjust (\cl -> cl{logonPassed = True, isAdministrator = True}) clID clients, rooms) [MoveToLobby, AnswerThisClient ["ADMIN_ACCESS"]] -processAction JoinLobby = do - chan <- client's sendChan - clientNick <- client's nick - (lobbyNicks, clientsChans) <- liftM (unzip . Prelude.map (\c -> (nick c, sendChan c)) . Prelude.filter logonPassed) $! allClientsS - mapM_ processAction $ - (AnswerClients clientsChans ["LOBBY:JOINED", clientNick]) - : [AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)] - ++ [ModifyClient (\cl -> cl{logonPassed = True}), SendServerMessage] +processAction (clID, serverInfo, clients, rooms) (MoveToLobby) = + foldM processAction (clID, serverInfo, clients, rooms) $ + (RoomAddThisClient 0) + : answerLobbyNicks + ++ [SendServerMessage] -{- -processAction (clID, serverInfo, rnc) (RoomAddThisClient rID) = - processAction ( - clID, - serverInfo, - adjust (\cl -> cl{roomID = rID, teamsInGame = if rID == 0 then teamsInGame cl else 0}) clID clients, - adjust (\r -> r{playersIDs = IntSet.insert clID (playersIDs r), playersIn = (playersIn r) + 1}) rID $ - adjust (\r -> r{playersIDs = IntSet.delete clID (playersIDs r)}) 0 rooms - ) joinMsg + -- ++ (answerServerMessage client clients) where - client = clients ! clID - joinMsg = if rID == 0 then - AnswerAllOthers ["LOBBY:JOINED", nick client] - else - AnswerThisRoom ["JOINED", nick client] - -processAction (clID, serverInfo, rnc) (KickClient kickID) = - liftM2 replaceID (return clID) (processAction (kickID, serverInfo, rnc) $ ByeClient "Kicked") + lobbyNicks = Prelude.map nick $ Prelude.filter logonPassed $ elems clients + answerLobbyNicks = [AnswerThisClient ("LOBBY:JOINED": lobbyNicks) | not $ Prelude.null lobbyNicks] -processAction (clID, serverInfo, rnc) (BanClient banNick) = - return (clID, serverInfo, rnc) +processAction (clID, serverInfo, clients, rooms) (KickClient kickID) = + liftM2 replaceID (return clID) (processAction (kickID, serverInfo, clients, rooms) $ ByeClient "Kicked") + + +processAction (clID, serverInfo, clients, rooms) (BanClient banNick) = + return (clID, serverInfo, clients, rooms) -processAction (clID, serverInfo, rnc) (KickRoomClient kickID) = do +processAction (clID, serverInfo, clients, rooms) (KickRoomClient kickID) = do writeChan (sendChan $ clients ! kickID) ["KICKED"] - liftM2 replaceID (return clID) (processAction (kickID, serverInfo, rnc) $ RoomRemoveThisClient "kicked") + liftM2 replaceID (return clID) (processAction (kickID, serverInfo, clients, rooms) $ RoomRemoveThisClient "kicked") -processAction (clID, serverInfo, rnc) (RemoveClientTeams teamsClID) = +processAction (clID, serverInfo, clients, rooms) (RemoveClientTeams teamsClID) = liftM2 replaceID (return clID) $ - foldM processAction (teamsClID, serverInfo, rnc) removeTeamsActions + foldM processAction (teamsClID, serverInfo, clients, rooms) removeTeamsActions where client = clients ! teamsClID room = rooms ! (roomID client) teamsToRemove = Prelude.filter (\t -> teamowner t == nick client) $ teams room removeTeamsActions = Prelude.map (RemoveTeam . teamname) teamsToRemove --} + -processAction (AddClient client) = do - rnc <- gets roomsClients - si <- gets serverInfo - liftIO $ do - ci <- addClient rnc client - forkIO $ clientRecvLoop (clientSocket client) (coreChan si) ci - forkIO $ clientSendLoop (clientSocket client) (sendChan client) ci +processAction (clID, serverInfo, clients, rooms) (AddClient client) = do + let updatedClients = insert (clientUID client) client clients + infoM "Clients" (show (clientUID client) ++ ": New client. Time: " ++ show (connectTime client)) + writeChan (sendChan client) ["CONNECTED", "Hedgewars server http://www.hedgewars.org/"] - infoM "Clients" (show ci ++ ": New client. Time: " ++ show (connectTime client)) - - processAction $ AnswerClients [sendChan client] ["CONNECTED", "Hedgewars server http://www.hedgewars.org/"] -{- let newLogins = takeWhile (\(_ , time) -> (connectTime client) `diffUTCTime` time <= 11) $ lastLogins serverInfo + let newLogins = takeWhile (\(_ , time) -> (connectTime client) `diffUTCTime` time <= 11) $ lastLogins serverInfo - if False && (isJust $ host client `Prelude.lookup` newLogins) then - processAction (ci, serverInfo{lastLogins = newLogins}, rnc) $ ByeClient "Reconnected too fast" - else - return (ci, serverInfo) --} - + if isJust $ host client `Prelude.lookup` newLogins then + processAction (clID, serverInfo{lastLogins = newLogins}, updatedClients, rooms) $ ByeClient "Reconnected too fast" + else + return (clID, serverInfo{lastLogins = (host client, connectTime client) : newLogins}, updatedClients, rooms) -processAction PingAll = do - rnc <- gets roomsClients - liftIO (allClientsM rnc) >>= mapM_ (kickTimeouted rnc) - cis <- liftIO $ allClientsM rnc - chans <- liftIO $ mapM (client'sM rnc sendChan) cis - liftIO $ mapM_ (modifyClient rnc (\cl -> cl{pingsQueue = pingsQueue cl + 1})) cis - processAction $ AnswerClients chans ["PING"] +processAction (clID, serverInfo, clients, rooms) PingAll = do + (_, _, newClients, newRooms) <- foldM kickTimeouted (clID, serverInfo, clients, rooms) $ elems clients + processAction (clID, + serverInfo, + Data.IntMap.map (\cl -> cl{pingsQueue = pingsQueue cl + 1}) newClients, + newRooms) $ AnswerAll ["PING"] where - kickTimeouted rnc ci = do - pq <- liftIO $ client'sM rnc pingsQueue ci - when (pq > 0) $ - withStateT (\as -> as{clientIndex = Just ci}) $ - processAction (ByeClient "Ping timeout") + kickTimeouted (clID, serverInfo, clients, rooms) client = + if pingsQueue client > 0 then + processAction (clientUID client, serverInfo, clients, rooms) $ ByeClient "Ping timeout" + else + return (clID, serverInfo, clients, rooms) -processAction (StatsAction) = do - rnc <- gets roomsClients - si <- gets serverInfo - (roomsNum, clientsNum) <- liftIO $ withRoomsAndClients rnc stats - liftIO $ writeChan (dbQueries si) $ SendStats clientsNum (roomsNum - 1) - where - stats irnc = (length $ allRooms irnc, length $ allClients irnc) - +processAction (clID, serverInfo, clients, rooms) (StatsAction) = do + writeChan (dbQueries serverInfo) $ SendStats (size clients) (size rooms - 1) + return (clID, serverInfo, clients, rooms) diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/CMakeLists.txt --- a/gameServer/CMakeLists.txt Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/CMakeLists.txt Thu Dec 23 17:47:50 2010 +0100 @@ -1,48 +1,43 @@ find_program(ghc_executable ghc) if(NOT ghc_executable) - message(FATAL_ERROR "Cannot find GHC") + message(FATAL_ERROR "Cannot find GHC") endif(NOT ghc_executable) set(hwserver_sources - OfficialServer/DBInteraction.hs - Actions.hs - ClientIO.hs - CoreTypes.hs - HWProtoCore.hs - HWProtoInRoomState.hs - HWProtoLobbyState.hs - HWProtoNEState.hs - HandlerUtils.hs - NetRoutines.hs - Opts.hs - RoomsAndClients.hs - ServerCore.hs - ServerState.hs - Store.hs - Utils.hs - hedgewars-server.hs - ) + OfficialServer/DBInteraction.hs + Actions.hs + ClientIO.hs + CoreTypes.hs + HWProtoCore.hs + HWProtoInRoomState.hs + HWProtoLobbyState.hs + HWProtoNEState.hs + NetRoutines.hs + Opts.hs + ServerCore.hs + Utils.hs + hedgewars-server.hs + ) set(hwserv_main ${hedgewars_SOURCE_DIR}/gameServer/hedgewars-server.hs) set(ghc_flags - -Wall - --make ${hwserv_main} - -i${hedgewars_SOURCE_DIR}/gameServer - -o ${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX} - -odir ${CMAKE_CURRENT_BINARY_DIR} - -hidir ${CMAKE_CURRENT_BINARY_DIR}) + --make ${hwserv_main} + -i${hedgewars_SOURCE_DIR}/gameServer + -o ${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX} + -odir ${CMAKE_CURRENT_BINARY_DIR} + -hidir ${CMAKE_CURRENT_BINARY_DIR}) set(ghc_flags ${haskell_compiler_flags_cmn} ${ghc_flags}) add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}" - COMMAND "${ghc_executable}" - ARGS ${ghc_flags} - MAIN_DEPENDENCY ${hwserv_main} - DEPENDS ${hwserver_sources} - ) + COMMAND "${ghc_executable}" + ARGS ${ghc_flags} + MAIN_DEPENDENCY ${hwserv_main} + DEPENDS ${hwserver_sources} + ) add_custom_target(hedgewars-server ALL DEPENDS "${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}") diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/ClientIO.hs --- a/gameServer/ClientIO.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/ClientIO.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,4 +1,4 @@ -{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} module ClientIO where import qualified Control.Exception as Exception @@ -6,71 +6,45 @@ import Control.Concurrent import Control.Monad import System.IO -import Network -import Network.Socket.ByteString -import qualified Data.ByteString.Char8 as B +import qualified Data.ByteString.UTF8 as BUTF8 +import qualified Data.ByteString as B ---------------- import CoreTypes -import RoomsAndClients -import Utils - -pDelim :: B.ByteString -pDelim = B.pack "\n\n" - -bs2Packets :: B.ByteString -> ([[B.ByteString]], B.ByteString) -bs2Packets buf = unfoldrE extractPackets buf - where - extractPackets :: B.ByteString -> Either B.ByteString ([B.ByteString], B.ByteString) - extractPackets buf = - let buf' = until (not . B.isPrefixOf pDelim) (B.drop 2) buf in - let (bsPacket, bufTail) = B.breakSubstring pDelim buf' in - if B.null bufTail then - Left bsPacket - else - if B.null bsPacket then - Left bufTail - else - Right (B.splitWith (== '\n') bsPacket, bufTail) - +listenLoop :: Handle -> Int -> [String] -> Chan CoreMessage -> Int -> IO () +listenLoop handle linesNumber buf chan clientID = do + str <- liftM BUTF8.toString $ B.hGetLine handle + if (linesNumber > 50) || (length str > 20000) then + writeChan chan $ ClientMessage (clientID, ["QUIT", "Protocol violation"]) + else + if str == "" then do + writeChan chan $ ClientMessage (clientID, buf) + yield + listenLoop handle 0 [] chan clientID + else + listenLoop handle (linesNumber + 1) (buf ++ [str]) chan clientID -listenLoop :: Socket -> Chan CoreMessage -> ClientIndex -> IO () -listenLoop sock chan ci = recieveWithBufferLoop B.empty - where - recieveWithBufferLoop recvBuf = do - recvBS <- recv sock 4096 --- putStrLn $ show sock ++ " got smth: " ++ (show $ B.length recvBS) - unless (B.null recvBS) $ do - let (packets, newrecvBuf) = bs2Packets $ B.append recvBuf recvBS - forM_ packets sendPacket - recieveWithBufferLoop newrecvBuf - - sendPacket packet = writeChan chan $ ClientMessage (ci, packet) - +clientRecvLoop :: Handle -> Chan CoreMessage -> Int -> IO () +clientRecvLoop handle chan clientID = + listenLoop handle 0 [] chan clientID + `catch` (\e -> clientOff (show e) >> return ()) + where clientOff msg = writeChan chan $ ClientMessage (clientID, ["QUIT", msg]) -- if the client disconnects, we perform as if it sent QUIT message -clientRecvLoop :: Socket -> Chan CoreMessage -> ClientIndex -> IO () -clientRecvLoop s chan ci = do - msg <- (listenLoop s chan ci >> return "Connection closed") `catch` (return . B.pack . show) - clientOff msg - where - clientOff msg = mapM_ (writeChan chan) [ClientMessage (ci, ["QUIT", msg]), Remove ci] - - +clientSendLoop :: Handle -> Chan CoreMessage -> Chan [String] -> Int -> IO() +clientSendLoop handle coreChan chan clientID = do + answer <- readChan chan + doClose <- Exception.handle + (\(e :: Exception.IOException) -> if isQuit answer then return True else sendQuit e >> return False) $ do + B.hPutStrLn handle $ BUTF8.fromString $ unlines answer + hFlush handle + return $ isQuit answer -clientSendLoop :: Socket -> Chan [B.ByteString] -> ClientIndex -> IO () -clientSendLoop s chan ci = do - answer <- readChan chan - Exception.handle - (\(e :: Exception.IOException) -> when (not $ isQuit answer) $ sendQuit e) $ do - sendAll s $ (B.unlines answer) `B.append` (B.singleton '\n') - - if (isQuit answer) then - Exception.handle (\(_ :: Exception.IOException) -> putStrLn "error on sClose") $ sClose s + if doClose then + Exception.handle (\(_ :: Exception.IOException) -> putStrLn "error on hClose") $ hClose handle else - clientSendLoop s chan ci + clientSendLoop handle coreChan chan clientID where - --sendQuit e = writeChan coreChan $ ClientMessage (ci, ["QUIT", B.pack $ show e]) - sendQuit e = putStrLn $ show e + sendQuit e = writeChan coreChan $ ClientMessage (clientID, ["QUIT", show e]) isQuit ("BYE":xs) = True isQuit _ = False diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/CoreTypes.hs --- a/gameServer/CoreTypes.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/CoreTypes.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,4 +1,3 @@ -{-# LANGUAGE OverloadedStrings #-} module CoreTypes where import System.IO @@ -6,95 +5,102 @@ import Control.Concurrent.STM import Data.Word import qualified Data.Map as Map +import qualified Data.IntMap as IntMap import qualified Data.IntSet as IntSet import Data.Sequence(Seq, empty) import Data.Time import Network import Data.Function -import Data.ByteString.Char8 as B -import RoomsAndClients - -type ClientChan = Chan [B.ByteString] data ClientInfo = ClientInfo { - sendChan :: ClientChan, - clientSocket :: Socket, - host :: B.ByteString, + clientUID :: !Int, + sendChan :: Chan [String], + clientHandle :: Handle, + host :: String, connectTime :: UTCTime, - nick :: B.ByteString, - webPassword :: B.ByteString, + nick :: String, + webPassword :: String, logonPassed :: Bool, clientProto :: !Word16, - roomID :: RoomIndex, + roomID :: !Int, pingsQueue :: !Word, isMaster :: Bool, - isReady :: !Bool, + isReady :: Bool, isAdministrator :: Bool, - clientClan :: B.ByteString, + clientClan :: String, teamsInGame :: Word } instance Show ClientInfo where - show ci = " nick: " ++ (unpack $ nick ci) ++ " host: " ++ (unpack $ host ci) + show ci = show (clientUID ci) + ++ " nick: " ++ (nick ci) + ++ " host: " ++ (host ci) instance Eq ClientInfo where - (==) = (==) `on` clientSocket + (==) = (==) `on` clientHandle data HedgehogInfo = - HedgehogInfo B.ByteString B.ByteString + HedgehogInfo String String data TeamInfo = TeamInfo { - teamownerId :: ClientIndex, - teamowner :: B.ByteString, - teamname :: B.ByteString, - teamcolor :: B.ByteString, - teamgrave :: B.ByteString, - teamfort :: B.ByteString, - teamvoicepack :: B.ByteString, - teamflag :: B.ByteString, + teamownerId :: !Int, + teamowner :: String, + teamname :: String, + teamcolor :: String, + teamgrave :: String, + teamfort :: String, + teamvoicepack :: String, + teamflag :: String, difficulty :: Int, hhnum :: Int, hedgehogs :: [HedgehogInfo] } instance Show TeamInfo where - show ti = "owner: " ++ (unpack $ teamowner ti) - ++ "name: " ++ (unpack $ teamname ti) - ++ "color: " ++ (unpack $ teamcolor ti) + show ti = "owner: " ++ (teamowner ti) + ++ "name: " ++ (teamname ti) + ++ "color: " ++ (teamcolor ti) data RoomInfo = RoomInfo { - masterID :: ClientIndex, - name :: B.ByteString, - password :: B.ByteString, + roomUID :: !Int, + masterID :: !Int, + name :: String, + password :: String, roomProto :: Word16, teams :: [TeamInfo], gameinprogress :: Bool, playersIn :: !Int, readyPlayers :: !Int, + playersIDs :: IntSet.IntSet, isRestrictedJoins :: Bool, isRestrictedTeams :: Bool, - roundMsgs :: Seq B.ByteString, - leftTeams :: [B.ByteString], + roundMsgs :: Seq String, + leftTeams :: [String], teamsAtStart :: [TeamInfo], - params :: Map.Map B.ByteString [B.ByteString] + params :: Map.Map String [String] } instance Show RoomInfo where - show ri = ", players: " ++ show (playersIn ri) + show ri = show (roomUID ri) + ++ ", players ids: " ++ show (IntSet.size $ playersIDs ri) + ++ ", players: " ++ show (playersIn ri) ++ ", ready: " ++ show (readyPlayers ri) ++ ", teams: " ++ show (teams ri) -newRoom :: RoomInfo +instance Eq RoomInfo where + (==) = (==) `on` roomUID + newRoom = ( RoomInfo - undefined + 0 + 0 "" "" 0 @@ -102,6 +108,7 @@ False 0 0 + IntSet.empty False False Data.Sequence.empty @@ -121,24 +128,23 @@ ServerInfo { isDedicated :: Bool, - serverMessage :: B.ByteString, - serverMessageForOldVersions :: B.ByteString, + serverMessage :: String, + serverMessageForOldVersions :: String, latestReleaseVersion :: Word16, listenPort :: PortNumber, nextRoomID :: Int, - dbHost :: B.ByteString, - dbLogin :: B.ByteString, - dbPassword :: B.ByteString, - lastLogins :: [(B.ByteString, UTCTime)], + dbHost :: String, + dbLogin :: String, + dbPassword :: String, + lastLogins :: [(String, UTCTime)], stats :: TMVar StatisticsInfo, coreChan :: Chan CoreMessage, dbQueries :: Chan DBQuery } instance Show ServerInfo where - show _ = "Server Info" + show si = "Server Info" -newServerInfo :: TMVar StatisticsInfo -> Chan CoreMessage -> Chan DBQuery -> ServerInfo newServerInfo = ( ServerInfo True @@ -154,31 +160,29 @@ ) data AccountInfo = - HasAccount B.ByteString Bool + HasAccount String Bool | Guest | Admin deriving (Show, Read) data DBQuery = - CheckAccount ClientIndex B.ByteString B.ByteString + CheckAccount Int String String | ClearCache | SendStats Int Int deriving (Show, Read) data CoreMessage = Accept ClientInfo - | ClientMessage (ClientIndex, [B.ByteString]) - | ClientAccountInfo (ClientIndex, AccountInfo) + | ClientMessage (Int, [String]) + | ClientAccountInfo (Int, AccountInfo) | TimerAction Int - | Remove ClientIndex + +type Clients = IntMap.IntMap ClientInfo +type Rooms = IntMap.IntMap RoomInfo -instance Show CoreMessage where - show (Accept _) = "Accept" - show (ClientMessage _) = "ClientMessage" - show (ClientAccountInfo _) = "ClientAccountInfo" - show (TimerAction _) = "TimerAction" - show (Remove _) = "Remove" - -type MRnC = MRoomsAndClients RoomInfo ClientInfo -type IRnC = IRoomsAndClients RoomInfo ClientInfo +--type ClientsTransform = [ClientInfo] -> [ClientInfo] +--type RoomsTransform = [RoomInfo] -> [RoomInfo] +--type HandlesSelector = ClientInfo -> [ClientInfo] -> [RoomInfo] -> [ClientInfo] +--type Answer = ServerInfo -> (HandlesSelector, [String]) +type ClientsSelector = Clients -> Rooms -> [Int] diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/HWProtoCore.hs --- a/gameServer/HWProtoCore.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/HWProtoCore.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,10 +1,8 @@ -{-# LANGUAGE OverloadedStrings #-} module HWProtoCore where import qualified Data.IntMap as IntMap import Data.Foldable import Data.Maybe -import Control.Monad.Reader -------------------------------------- import CoreTypes import Actions @@ -12,37 +10,35 @@ import HWProtoNEState import HWProtoLobbyState import HWProtoInRoomState -import HandlerUtils -import RoomsAndClients handleCmd, handleCmd_loggedin :: CmdHandler - -handleCmd ["PING"] = answerClient ["PONG"] +handleCmd clID _ _ ["PING"] = [AnswerThisClient ["PONG"]] - -handleCmd ("QUIT" : xs) = return [ByeClient msg] +handleCmd clID clients rooms ("QUIT" : xs) = + [ByeClient msg] where msg = if not $ null xs then head xs else "" -{- -handleCmd ["PONG"] = + +handleCmd clID clients _ ["PONG"] = if pingsQueue client == 0 then [ProtocolError "Protocol violation"] else [ModifyClient (\cl -> cl{pingsQueue = pingsQueue cl - 1})] where client = clients IntMap.! clID --} + -handleCmd cmd = do - (ci, irnc) <- ask - if logonPassed (irnc `client` ci) then - handleCmd_loggedin cmd - else - handleCmd_NotEntered cmd +handleCmd clID clients rooms cmd = + if not $ logonPassed client then + handleCmd_NotEntered clID clients rooms cmd + else + handleCmd_loggedin clID clients rooms cmd + where + client = clients IntMap.! clID -{- + handleCmd_loggedin clID clients rooms ["INFO", asknick] = if noSuchClient then [] @@ -66,12 +62,11 @@ then if teamsInGame client > 0 then "(playing)" else "(spectating)" else "" --} - -handleCmd_loggedin cmd = do - (ci, rnc) <- ask - if clientRoom rnc ci == lobbyId then - handleCmd_lobby cmd - else - handleCmd_inRoom cmd +handleCmd_loggedin clID clients rooms cmd = + if roomID client == 0 then + handleCmd_lobby clID clients rooms cmd + else + handleCmd_inRoom clID clients rooms cmd + where + client = clients IntMap.! clID diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/HWProtoInRoomState.hs --- a/gameServer/HWProtoInRoomState.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/HWProtoInRoomState.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,240 +1,182 @@ -{-# LANGUAGE OverloadedStrings #-} module HWProtoInRoomState where import qualified Data.Foldable as Foldable +import qualified Data.IntMap as IntMap import qualified Data.Map as Map import Data.Sequence(Seq, (|>), (><), fromList, empty) import Data.List import Data.Maybe -import qualified Data.ByteString.Char8 as B -import Control.Monad -import Control.Monad.Reader -------------------------------------- import CoreTypes import Actions import Utils -import HandlerUtils -import RoomsAndClients + handleCmd_inRoom :: CmdHandler -handleCmd_inRoom ["CHAT", msg] = do - n <- clientNick - s <- roomOthersChans - return [AnswerClients s ["CHAT", n, msg]] +handleCmd_inRoom clID clients _ ["CHAT", msg] = + [AnswerOthersInRoom ["CHAT", clientNick, msg]] + where + clientNick = nick $ clients IntMap.! clID -handleCmd_inRoom ["PART"] = return [MoveToLobby "part"] -handleCmd_inRoom ["PART", msg] = return [MoveToLobby $ "part: " `B.append` msg] +handleCmd_inRoom clID clients rooms ["PART"] = + [RoomRemoveThisClient "part"] + where + client = clients IntMap.! clID -handleCmd_inRoom ("CFG" : paramName : paramStrs) - | null paramStrs = return [ProtocolError "Empty config entry"] - | otherwise = do - chans <- roomOthersChans - cl <- thisClient - if isMaster cl then - return [ - ModifyRoom (\r -> r{params = Map.insert paramName paramStrs (params r)}), - AnswerClients chans ("CFG" : paramName : paramStrs)] - else - return [ProtocolError "Not room master"] +handleCmd_inRoom clID clients rooms ("CFG" : paramName : paramStrs) + | null paramStrs = [ProtocolError "Empty config entry"] + | isMaster client = + [ModifyRoom (\r -> r{params = Map.insert paramName paramStrs (params r)}), + AnswerOthersInRoom ("CFG" : paramName : paramStrs)] + | otherwise = [ProtocolError "Not room master"] + where + client = clients IntMap.! clID -handleCmd_inRoom ("ADD_TEAM" : name : color : grave : fort : voicepack : flag : difStr : hhsInfo) - | length hhsInfo /= 16 = return [ProtocolError "Corrupted hedgehogs info"] - | otherwise = do - (ci, rnc) <- ask - r <- thisRoom - clNick <- clientNick - clChan <- thisClientChans - othersChans <- roomOthersChans - return $ - if not . null . drop 5 $ teams r then - [Warning "too many teams"] - else if canAddNumber r <= 0 then - [Warning "too many hedgehogs"] - else if isJust $ findTeam r then - [Warning "There's already a team with same name in the list"] - else if gameinprogress r then - [Warning "round in progress"] - else if isRestrictedTeams r then - [Warning "restricted"] - else - [ModifyRoom (\r -> r{teams = teams r ++ [newTeam ci clNick r]}), - ModifyClient (\c -> c{teamsInGame = teamsInGame c + 1, clientClan = color}), - AnswerClients clChan ["TEAM_ACCEPTED", name], - AnswerClients othersChans $ teamToNet $ newTeam ci clNick r, - AnswerClients othersChans ["TEAM_COLOR", name, color] - ] - where - canAddNumber r = 48 - (sum . map hhnum $ teams r) - findTeam = find (\t -> name == teamname t) . teams - newTeam ci clNick r = (TeamInfo ci clNick name color grave fort voicepack flag difficulty (newTeamHHNum r) (hhsList hhsInfo)) - difficulty = case B.readInt difStr of - Just (i, t) | B.null t -> fromIntegral i - otherwise -> 0 +handleCmd_inRoom clID clients rooms ("ADD_TEAM" : name : color : grave : fort : voicepack : flag : difStr : hhsInfo) + | length hhsInfo == 15 && clientProto client < 30 = handleCmd_inRoom clID clients rooms ("ADD_TEAM" : name : color : grave : fort : voicepack : " " : flag : difStr : hhsInfo) + | length hhsInfo /= 16 = [ProtocolError "Corrupted hedgehogs info"] + | length (teams room) == 6 = [Warning "too many teams"] + | canAddNumber <= 0 = [Warning "too many hedgehogs"] + | isJust findTeam = [Warning "There's already a team with same name in the list"] + | gameinprogress room = [Warning "round in progress"] + | isRestrictedTeams room = [Warning "restricted"] + | otherwise = + [ModifyRoom (\r -> r{teams = teams r ++ [newTeam]}), + ModifyClient (\c -> c{teamsInGame = teamsInGame c + 1, clientClan = color}), + AnswerThisClient ["TEAM_ACCEPTED", name], + AnswerOthersInRoom $ teamToNet (clientProto client) newTeam, + AnswerOthersInRoom ["TEAM_COLOR", name, color] + ] + where + client = clients IntMap.! clID + room = rooms IntMap.! (roomID client) + canAddNumber = 48 - (sum . map hhnum $ teams room) + findTeam = find (\t -> name == teamname t) $ teams room + newTeam = (TeamInfo clID (nick client) name color grave fort voicepack flag difficulty newTeamHHNum (hhsList hhsInfo)) + difficulty = fromMaybe 0 (maybeRead difStr :: Maybe Int) hhsList [] = [] - hhsList [_] = error "Hedgehogs list with odd elements number" hhsList (n:h:hhs) = HedgehogInfo n h : hhsList hhs - newTeamHHNum r = min 4 (canAddNumber r) - -handleCmd_inRoom ["REMOVE_TEAM", name] = do - (ci, rnc) <- ask - r <- thisRoom - clNick <- clientNick - - let maybeTeam = findTeam r - let team = fromJust maybeTeam + newTeamHHNum = min 4 canAddNumber - return $ - if isNothing $ findTeam r then - [Warning "REMOVE_TEAM: no such team"] - else if clNick /= teamowner team then - [ProtocolError "Not team owner!"] - else - [RemoveTeam name, - ModifyClient - (\c -> c{ - teamsInGame = teamsInGame c - 1, - clientClan = if teamsInGame c == 1 then undefined else anotherTeamClan ci r - }) - ] +handleCmd_inRoom clID clients rooms ["REMOVE_TEAM", teamName] + | noSuchTeam = [Warning "REMOVE_TEAM: no such team"] + | nick client /= teamowner team = [ProtocolError "Not team owner!"] + | otherwise = + [RemoveTeam teamName, + ModifyClient (\c -> c{teamsInGame = teamsInGame c - 1, clientClan = if teamsInGame client == 1 then undefined else anotherTeamClan}) + ] where - anotherTeamClan ci = teamcolor . fromJust . find (\t -> teamownerId t == ci) . teams - findTeam = find (\t -> name == teamname t) . teams + client = clients IntMap.! clID + room = rooms IntMap.! (roomID client) + noSuchTeam = isNothing findTeam + team = fromJust findTeam + findTeam = find (\t -> teamName == teamname t) $ teams room + anotherTeamClan = teamcolor $ fromJust $ find (\t -> teamownerId t == clID) $ teams room -handleCmd_inRoom ["HH_NUM", teamName, numberStr] = do - cl <- thisClient - others <- roomOthersChans - r <- thisRoom - - let maybeTeam = findTeam r - let team = fromJust maybeTeam - - return $ - if not $ isMaster cl then - [ProtocolError "Not room master"] - else if hhNumber < 1 || hhNumber > 8 || isNothing maybeTeam || hhNumber > (canAddNumber r) + (hhnum team) then - [] - else - [ModifyRoom $ modifyTeam team{hhnum = hhNumber}, - AnswerClients others ["HH_NUM", teamName, B.pack $ show hhNumber]] +handleCmd_inRoom clID clients rooms ["HH_NUM", teamName, numberStr] + | not $ isMaster client = [ProtocolError "Not room master"] + | hhNumber < 1 || hhNumber > 8 || noSuchTeam || hhNumber > (canAddNumber + (hhnum team)) = [] + | otherwise = + [ModifyRoom $ modifyTeam team{hhnum = hhNumber}, + AnswerOthersInRoom ["HH_NUM", teamName, show hhNumber]] where - hhNumber = case B.readInt numberStr of - Just (i, t) | B.null t -> fromIntegral i - otherwise -> 0 - findTeam = find (\t -> teamName == teamname t) . teams - canAddNumber = (-) 48 . sum . map hhnum . teams - + client = clients IntMap.! clID + room = rooms IntMap.! (roomID client) + hhNumber = fromMaybe 0 (maybeRead numberStr :: Maybe Int) + noSuchTeam = isNothing findTeam + team = fromJust findTeam + findTeam = find (\t -> teamName == teamname t) $ teams room + canAddNumber = 48 - (sum . map hhnum $ teams room) -handleCmd_inRoom ["TEAM_COLOR", teamName, newColor] = do - cl <- thisClient - others <- roomOthersChans - r <- thisRoom - - let maybeTeam = findTeam r - let team = fromJust maybeTeam - - return $ - if not $ isMaster cl then - [ProtocolError "Not room master"] - else if isNothing maybeTeam then - [] - else - [ModifyRoom $ modifyTeam team{teamcolor = newColor}, - AnswerClients others ["TEAM_COLOR", teamName, newColor], +handleCmd_inRoom clID clients rooms ["TEAM_COLOR", teamName, newColor] + | not $ isMaster client = [ProtocolError "Not room master"] + | noSuchTeam = [] + | otherwise = [ModifyRoom $ modifyTeam team{teamcolor = newColor}, + AnswerOthersInRoom ["TEAM_COLOR", teamName, newColor], ModifyClient2 (teamownerId team) (\c -> c{clientClan = newColor})] where - findTeam = find (\t -> teamName == teamname t) . teams + noSuchTeam = isNothing findTeam + team = fromJust findTeam + findTeam = find (\t -> teamName == teamname t) $ teams room + client = clients IntMap.! clID + room = rooms IntMap.! (roomID client) -handleCmd_inRoom ["TOGGLE_READY"] = do - cl <- thisClient - chans <- roomClientsChans - return [ - ModifyClient (\c -> c{isReady = not $ isReady cl}), - ModifyRoom (\r -> r{readyPlayers = readyPlayers r + (if isReady cl then -1 else 1)}), - AnswerClients chans [if isReady cl then "NOT_READY" else "READY", nick cl] - ] +handleCmd_inRoom clID clients rooms ["TOGGLE_READY"] = + [ModifyClient (\c -> c{isReady = not $ isReady client}), + ModifyRoom (\r -> r{readyPlayers = readyPlayers r + (if isReady client then -1 else 1)}), + AnswerThisRoom [if isReady client then "NOT_READY" else "READY", nick client]] + where + client = clients IntMap.! clID -handleCmd_inRoom ["START_GAME"] = do - cl <- thisClient - r <- thisRoom - chans <- roomClientsChans - if isMaster cl && (playersIn r == readyPlayers r) && (not $ gameinprogress r) then - if enoughClans r then - return [ - ModifyRoom +handleCmd_inRoom clID clients rooms ["START_GAME"] = + if isMaster client && (playersIn room == readyPlayers room) && (not . gameinprogress) room then + if enoughClans then + [ModifyRoom (\r -> r{ gameinprogress = True, roundMsgs = empty, leftTeams = [], teamsAtStart = teams r} ), - AnswerClients chans ["RUN_GAME"] - ] - else - return [Warning "Less than two clans!"] + AnswerThisRoom ["RUN_GAME"]] else - return [] + [Warning "Less than two clans!"] + else + [] where - enoughClans = not . null . drop 1 . group . map teamcolor . teams + client = clients IntMap.! clID + room = rooms IntMap.! (roomID client) + enoughClans = not $ null $ drop 1 $ group $ map teamcolor $ teams room -handleCmd_inRoom ["EM", msg] = do - cl <- thisClient - r <- thisRoom - chans <- roomOthersChans - - if (teamsInGame cl > 0) && isLegal then - return $ (AnswerClients chans ["EM", msg]) : [ModifyRoom (\r -> r{roundMsgs = roundMsgs r |> msg}) | not isKeepAlive] - else - return [] +handleCmd_inRoom clID clients rooms ["EM", msg] = + if (teamsInGame client > 0) && isLegal then + (AnswerOthersInRoom ["EM", msg]) : [ModifyRoom (\r -> r{roundMsgs = roundMsgs r |> msg}) | not isKeepAlive] + else + [] where + client = clients IntMap.! clID (isLegal, isKeepAlive) = checkNetCmd msg - -handleCmd_inRoom ["ROUNDFINISHED"] = do - cl <- thisClient - r <- thisRoom - chans <- roomClientsChans - - if isMaster cl && (gameinprogress r) then - return $ (ModifyRoom +handleCmd_inRoom clID clients rooms ["ROUNDFINISHED"] = + if isMaster client then + [ModifyRoom (\r -> r{ gameinprogress = False, readyPlayers = 0, roundMsgs = empty, leftTeams = [], teamsAtStart = []} - )) - : UnreadyRoomClients - : answerRemovedTeams chans r - else - return [] + ), + UnreadyRoomClients + ] ++ answerRemovedTeams + else + [] where - answerRemovedTeams chans = map (\t -> AnswerClients chans ["REMOVE_TEAM", t]) . leftTeams - -handleCmd_inRoom ["TOGGLE_RESTRICT_JOINS"] = do - cl <- thisClient - return $ - if not $ isMaster cl then - [ProtocolError "Not room master"] - else - [ModifyRoom (\r -> r{isRestrictedJoins = not $ isRestrictedJoins r})] + client = clients IntMap.! clID + room = rooms IntMap.! (roomID client) + answerRemovedTeams = map (\t -> AnswerThisRoom ["REMOVE_TEAM", t]) $ leftTeams room -handleCmd_inRoom ["TOGGLE_RESTRICT_TEAMS"] = do - cl <- thisClient - return $ - if not $ isMaster cl then - [ProtocolError "Not room master"] - else - [ModifyRoom (\r -> r{isRestrictedTeams = not $ isRestrictedTeams r})] +handleCmd_inRoom clID clients _ ["TOGGLE_RESTRICT_JOINS"] + | isMaster client = [ModifyRoom (\r -> r{isRestrictedJoins = not $ isRestrictedJoins r})] + | otherwise = [ProtocolError "Not room master"] + where + client = clients IntMap.! clID + -{- +handleCmd_inRoom clID clients _ ["TOGGLE_RESTRICT_TEAMS"] + | isMaster client = [ModifyRoom (\r -> r{isRestrictedTeams = not $ isRestrictedTeams r})] + | otherwise = [ProtocolError "Not room master"] + where + client = clients IntMap.! clID + handleCmd_inRoom clID clients rooms ["KICK", kickNick] = [KickRoomClient kickID | isMaster client && not noSuchClient && (kickID /= clID) && (roomID client == roomID kickClient)] where @@ -250,5 +192,5 @@ where client = clients IntMap.! clID engineMsg = toEngineMsg $ 'b' : ((nick client) ++ "(team): " ++ msg ++ "\x20\x20") --} -handleCmd_inRoom _ = return [ProtocolError "Incorrect command (state: in room)"] + +handleCmd_inRoom clID _ _ _ = [ProtocolError "Incorrect command (state: in room)"] diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/HWProtoLobbyState.hs --- a/gameServer/HWProtoLobbyState.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/HWProtoLobbyState.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,102 +1,73 @@ -{-# LANGUAGE OverloadedStrings #-} module HWProtoLobbyState where import qualified Data.Map as Map +import qualified Data.IntMap as IntMap import qualified Data.IntSet as IntSet import qualified Data.Foldable as Foldable import Data.Maybe import Data.List import Data.Word -import Control.Monad.Reader -import qualified Data.ByteString.Char8 as B -------------------------------------- import CoreTypes import Actions import Utils -import HandlerUtils -import RoomsAndClients -{-answerAllTeams protocol teams = concatMap toAnswer teams +answerAllTeams protocol teams = concatMap toAnswer teams where toAnswer team = [AnswerThisClient $ teamToNet protocol team, AnswerThisClient ["TEAM_COLOR", teamname team, teamcolor team], AnswerThisClient ["HH_NUM", teamname team, show $ hhnum team]] --} + handleCmd_lobby :: CmdHandler - -handleCmd_lobby ["LIST"] = do - (ci, irnc) <- ask - let cl = irnc `client` ci - rooms <- allRoomInfos - let roomsInfoList = concatMap (roomInfo irnc) . filter (\r -> (roomProto r == clientProto cl) && not (isRestrictedJoins r)) - return [AnswerClients [sendChan cl] ("ROOMS" : roomsInfoList rooms)] +handleCmd_lobby clID clients rooms ["LIST"] = + [AnswerThisClient ("ROOMS" : roomsInfoList)] where - roomInfo irnc room = [ - showB $ gameinprogress room, + roomsInfoList = concatMap roomInfo sameProtoRooms + sameProtoRooms = filter (\r -> (roomProto r == protocol) && not (isRestrictedJoins r)) roomsList + roomsList = IntMap.elems rooms + protocol = clientProto client + client = clients IntMap.! clID + roomInfo room + | clientProto client < 28 = [ name room, - showB $ playersIn room, - showB $ length $ teams room, - nick $ irnc `client` masterID room, + show (playersIn room) ++ "(" ++ show (length $ teams room) ++ ")", + show $ gameinprogress room + ] + | otherwise = [ + show $ gameinprogress room, + name room, + show $ playersIn room, + show $ length $ teams room, + nick $ clients IntMap.! (masterID room), head (Map.findWithDefault ["+gen+"] "MAP" (params room)), head (Map.findWithDefault ["Default"] "SCHEME" (params room)), head (Map.findWithDefault ["Default"] "AMMO" (params room)) ] - -handleCmd_lobby ["CHAT", msg] = do - n <- clientNick - s <- roomOthersChans - return [AnswerClients s ["CHAT", n, msg]] - -handleCmd_lobby ["CREATE_ROOM", newRoom, roomPassword] - | illegalName newRoom = return [Warning "Illegal room name"] - | otherwise = do - rs <- allRoomInfos - cl <- thisClient - return $ if isJust $ find (\room -> newRoom == name room) rs then - [Warning "Room exists"] - else - [ - AddRoom newRoom roomPassword, - AnswerClients [sendChan cl] ["NOT_READY", nick cl] - ] - - -handleCmd_lobby ["CREATE_ROOM", newRoom] = - handleCmd_lobby ["CREATE_ROOM", newRoom, ""] +handleCmd_lobby clID clients _ ["CHAT", msg] = + [AnswerOthersInRoom ["CHAT", clientNick, msg]] + where + clientNick = nick $ clients IntMap.! clID -handleCmd_lobby ["JOIN_ROOM", roomName, roomPassword] = do - (ci, irnc) <- ask - let ris = allRooms irnc - cl <- thisClient - let maybeRI = find (\ri -> roomName == name (irnc `room` ri)) ris - let jRI = fromJust maybeRI - let jRoom = irnc `room` jRI - let jRoomClients = map (client irnc) $! roomClients irnc jRI -- no lazyness here! - return $ - if isNothing maybeRI then - [Warning "No such rooms"] - else if isRestrictedJoins jRoom then - [Warning "Joining restricted"] - else if roomPassword /= password jRoom then - [Warning "Wrong password"] - else - [ - MoveToRoom jRI, - AnswerClients (map sendChan $ cl : jRoomClients) ["NOT_READY", nick cl] - ] - ++ [ AnswerClients [sendChan cl] $ "JOINED" : map nick jRoomClients | playersIn jRoom /= 0] - ++ (map (readynessMessage cl) jRoomClients) - +handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom, roomPassword] + | haveSameRoom = [Warning "Room exists"] + | illegalName newRoom = [Warning "Illegal room name"] + | otherwise = + [RoomRemoveThisClient "", -- leave lobby + AddRoom newRoom roomPassword, + AnswerThisClient ["NOT_READY", clientNick] + ] where - readynessMessage cl c = AnswerClients [sendChan cl] [if isReady c then "READY" else "NOT_READY", nick c] + clientNick = nick $ clients IntMap.! clID + haveSameRoom = isJust $ find (\room -> newRoom == name room) $ IntMap.elems rooms +handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom] = + handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom, ""] -{- handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName, roomPassword] | noSuchRoom = [Warning "No such room"] @@ -112,6 +83,12 @@ ++ answerTeams ++ watchRound where + noSuchRoom = isNothing mbRoom + mbRoom = find (\r -> roomName == name r && roomProto r == clientProto client) $ IntMap.elems rooms + jRoom = fromJust mbRoom + rID = roomUID jRoom + client = clients IntMap.! clID + roomClientsIDs = IntSet.elems $ playersIDs jRoom answerNicks = [AnswerThisClient $ "JOINED" : map (\clID -> nick $ clients IntMap.! clID) roomClientsIDs | playersIn jRoom /= 0] @@ -123,9 +100,9 @@ roomClientsIDs toAnswer (paramName, paramStrs) = AnswerThisClient $ "CFG" : paramName : paramStrs - - answerFullConfig = map toAnswer (leftConfigPart ++ rightConfigPart) - (leftConfigPart, rightConfigPart) = partition (\(p, _) -> p /= "MAP") (Map.toList $ params jRoom) + + answerFullConfig = map toAnswer ((Data.List.reverse . Data.List.sort $ leftConfigPart) ++ rightConfigPart) + (leftConfigPart, rightConfigPart) = partition (\(p, _) -> p == "MAP" || p == "MAPGEN") (Map.toList $ params jRoom) watchRound = if not $ gameinprogress jRoom then [] @@ -137,12 +114,12 @@ answerAllTeams (clientProto client) (teamsAtStart jRoom) else answerAllTeams (clientProto client) (teams jRoom) --} + -handleCmd_lobby ["JOIN_ROOM", roomName] = - handleCmd_lobby ["JOIN_ROOM", roomName, ""] +handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName] = + handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName, ""] + -{- handleCmd_lobby clID clients rooms ["FOLLOW", asknick] = if noSuchClient || roomID followClient == 0 then [] @@ -203,7 +180,6 @@ [ClearAccountsCache | isAdministrator client] where client = clients IntMap.! clID --} -handleCmd_lobby _ = return [ProtocolError "Incorrect command (state: in lobby)"] +handleCmd_lobby clID _ _ _ = [ProtocolError "Incorrect command (state: in lobby)"] diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/HWProtoNEState.hs --- a/gameServer/HWProtoNEState.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/HWProtoNEState.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,66 +1,54 @@ -{-# LANGUAGE OverloadedStrings #-} module HWProtoNEState where import qualified Data.IntMap as IntMap import Data.Maybe import Data.List import Data.Word -import Control.Monad.Reader -import qualified Data.ByteString.Char8 as B -------------------------------------- import CoreTypes import Actions import Utils -import RoomsAndClients handleCmd_NotEntered :: CmdHandler -handleCmd_NotEntered ["NICK", newNick] = do - (ci, irnc) <- ask - let cl = irnc `client` ci - if not . B.null $ nick cl then return [ProtocolError "Nickname already chosen"] - else - if haveSameNick irnc (nick cl) then return [AnswerClients [sendChan cl] ["WARNING", "Nickname already in use"], ByeClient ""] - else - if illegalName newNick then return [ByeClient "Illegal nickname"] - else - return $ - ModifyClient (\c -> c{nick = newNick}) : - AnswerClients [sendChan cl] ["NICK", newNick] : - [CheckRegistered | clientProto cl /= 0] +handleCmd_NotEntered clID clients _ ["NICK", newNick] + | not . null $ nick client = [ProtocolError "Nickname already chosen"] + | haveSameNick = [AnswerThisClient ["WARNING", "Nickname already in use"], ByeClient ""] + | illegalName newNick = [ByeClient "Illegal nickname"] + | otherwise = + ModifyClient (\c -> c{nick = newNick}) : + AnswerThisClient ["NICK", newNick] : + [CheckRegistered | clientProto client /= 0] where - haveSameNick irnc clNick = isJust $ find (\cl -> newNick == clNick) $ map (client irnc) $ allClients irnc - -handleCmd_NotEntered ["PROTO", protoNum] = do - (ci, irnc) <- ask - let cl = irnc `client` ci - if clientProto cl > 0 then return [ProtocolError "Protocol already known"] - else - if parsedProto == 0 then return [ProtocolError "Bad number"] - else - return $ - ModifyClient (\c -> c{clientProto = parsedProto}) : - AnswerClients [sendChan cl] ["PROTO", B.pack $ show parsedProto] : - [CheckRegistered | not . B.null $ nick cl] - where - parsedProto = case B.readInt protoNum of - Just (i, t) | B.null t -> fromIntegral i - otherwise -> 0 + client = clients IntMap.! clID + haveSameNick = isJust $ find (\cl -> newNick == nick cl) $ IntMap.elems clients -handleCmd_NotEntered ["PASSWORD", passwd] = do - (ci, irnc) <- ask - let cl = irnc `client` ci +handleCmd_NotEntered clID clients _ ["PROTO", protoNum] + | clientProto client > 0 = [ProtocolError "Protocol already known"] + | parsedProto == 0 = [ProtocolError "Bad number"] + | otherwise = + ModifyClient (\c -> c{clientProto = parsedProto}) : + AnswerThisClient ["PROTO", show parsedProto] : + [CheckRegistered | (not . null) (nick client)] + where + client = clients IntMap.! clID + parsedProto = fromMaybe 0 (maybeRead protoNum :: Maybe Word16) - if passwd == webPassword cl then - return $ JoinLobby : [AnswerClients [sendChan cl] ["ADMIN_ACCESS"] | isAdministrator cl] - else - return [ByeClient "Authentication failed"] -{- +handleCmd_NotEntered clID clients _ ["PASSWORD", passwd] = + if passwd == webPassword client then + [ModifyClient (\cl -> cl{logonPassed = True}), + MoveToLobby] ++ adminNotice + else + [ByeClient "Authentication failed"] + where + client = clients IntMap.! clID + adminNotice = [AnswerThisClient ["ADMIN_ACCESS"] | isAdministrator client] + handleCmd_NotEntered clID clients _ ["DUMP"] = if isAdministrator (clients IntMap.! clID) then [Dump] else [] --} + -handleCmd_NotEntered _ = return [ProtocolError "Incorrect command (state: not entered)"] +handleCmd_NotEntered clID _ _ _ = [ProtocolError "Incorrect command (state: not entered)"] diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/HandlerUtils.hs --- a/gameServer/HandlerUtils.hs Tue Dec 14 22:32:47 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -module HandlerUtils where - -import Control.Monad.Reader -import qualified Data.ByteString.Char8 as B - -import RoomsAndClients -import CoreTypes -import Actions - -thisClient :: Reader (ClientIndex, IRnC) ClientInfo -thisClient = do - (ci, rnc) <- ask - return $ rnc `client` ci - -thisRoom :: Reader (ClientIndex, IRnC) RoomInfo -thisRoom = do - (ci, rnc) <- ask - let ri = clientRoom rnc ci - return $ rnc `room` ri - -clientNick :: Reader (ClientIndex, IRnC) B.ByteString -clientNick = liftM nick thisClient - -roomOthersChans :: Reader (ClientIndex, IRnC) [ClientChan] -roomOthersChans = do - (ci, rnc) <- ask - let ri = clientRoom rnc ci - return $ map (sendChan . client rnc) $ filter (/= ci) (roomClients rnc ri) - -roomClientsChans :: Reader (ClientIndex, IRnC) [ClientChan] -roomClientsChans = do - (ci, rnc) <- ask - let ri = clientRoom rnc ci - return $ map (sendChan . client rnc) (roomClients rnc ri) - -thisClientChans :: Reader (ClientIndex, IRnC) [ClientChan] -thisClientChans = do - (ci, rnc) <- ask - return $ [sendChan (rnc `client` ci)] - -answerClient :: [B.ByteString] -> Reader (ClientIndex, IRnC) [Action] -answerClient msg = thisClientChans >>= return . (: []) . flip AnswerClients msg - -allRoomInfos :: Reader (a, IRnC) [RoomInfo] -allRoomInfos = liftM ((\irnc -> map (room irnc) $ allRooms irnc) . snd) ask diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/NetRoutines.hs --- a/gameServer/NetRoutines.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/NetRoutines.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,41 +1,46 @@ -{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} module NetRoutines where +import Network import Network.Socket import System.IO +import Control.Concurrent import Control.Concurrent.Chan +import Control.Concurrent.STM import qualified Control.Exception as Exception import Data.Time -import Control.Monad ----------------------------- import CoreTypes +import ClientIO import Utils -import RoomsAndClients -acceptLoop :: Socket -> Chan CoreMessage -> IO () -acceptLoop servSock chan = forever $ do +acceptLoop :: Socket -> Chan CoreMessage -> Int -> IO () +acceptLoop servSock coreChan clientCounter = do Exception.handle (\(_ :: Exception.IOException) -> putStrLn "exception on connect") $ do - (sock, sockAddr) <- Network.Socket.accept servSock + (socket, sockAddr) <- Network.Socket.accept servSock + cHandle <- socketToHandle socket ReadWriteMode + hSetBuffering cHandle LineBuffering clientHost <- sockAddr2String sockAddr currentTime <- getCurrentTime - - sendChan' <- newChan + + sendChan <- newChan let newClient = (ClientInfo - sendChan' - sock + nextID + sendChan + cHandle clientHost currentTime "" "" False 0 - lobbyId + 0 0 False False @@ -44,5 +49,12 @@ undefined ) - writeChan chan $ Accept newClient + writeChan coreChan $ Accept newClient + + forkIO $ clientRecvLoop cHandle coreChan nextID + forkIO $ clientSendLoop cHandle coreChan sendChan nextID return () + + acceptLoop servSock coreChan nextID + where + nextID = clientCounter + 1 diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/OfficialServer/DBInteraction.hs --- a/gameServer/OfficialServer/DBInteraction.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/OfficialServer/DBInteraction.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP, ScopedTypeVariables, OverloadedStrings #-} +{-# LANGUAGE CPP, ScopedTypeVariables #-} module OfficialServer.DBInteraction ( startDBConnection @@ -20,7 +20,7 @@ localAddressList = ["127.0.0.1", "0:0:0:0:0:0:0:1", "0:0:0:0:0:ffff:7f00:1"] -fakeDbConnection serverInfo = forever $ do +fakeDbConnection serverInfo = do q <- readChan $ dbQueries serverInfo case q of CheckAccount clUid _ clHost -> do @@ -29,6 +29,8 @@ ClearCache -> return () SendStats {} -> return () + fakeDbConnection serverInfo + #if defined(OFFICIAL_SERVER) pipeDbConnectionLoop queries coreChan hIn hOut accountsCache = diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/OfficialServer/extdbinterface.hs --- a/gameServer/OfficialServer/extdbinterface.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/OfficialServer/extdbinterface.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,4 +1,4 @@ -{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} module Main where @@ -26,7 +26,7 @@ case q of CheckAccount clUid clNick _ -> do statement <- prepare dbConn dbQueryAccount - execute statement [SqlByteString $ clNick] + execute statement [SqlString $ clNick] passAndRole <- fetchRow statement finish statement let response = @@ -47,7 +47,7 @@ dbConnectionLoop mySQLConnectionInfo = - Control.Exception.handle (\(e :: IOException) -> hPutStrLn stderr $ show e) $ handleSqlError $ + Control.Exception.handle (\(_ :: IOException) -> return ()) $ handleSqlError $ bracket (connectMySQL mySQLConnectionInfo) (disconnect) diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/Opts.hs --- a/gameServer/Opts.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/Opts.hs Thu Dec 23 17:47:50 2010 +0100 @@ -3,12 +3,10 @@ getOpts, ) where -import System.Environment +import System.Environment ( getArgs ) import System.Console.GetOpt import Network import Data.Maybe ( fromMaybe ) -import qualified Data.ByteString.Char8 as B - import CoreTypes import Utils @@ -32,9 +30,9 @@ where readDedicated = fromMaybe True (maybeRead str :: Maybe Bool) -readDbLogin str opts = opts{dbLogin = B.pack str} -readDbPassword str opts = opts{dbPassword = B.pack str} -readDbHost str opts = opts{dbHost = B.pack str} +readDbLogin str opts = opts{dbLogin = str} +readDbPassword str opts = opts{dbPassword = str} +readDbHost str opts = opts{dbHost = str} getOpts :: ServerInfo -> IO ServerInfo getOpts opts = do diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/RoomsAndClients.hs --- a/gameServer/RoomsAndClients.hs Tue Dec 14 22:32:47 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,196 +0,0 @@ -module RoomsAndClients( - RoomIndex(), - ClientIndex(), - MRoomsAndClients(), - IRoomsAndClients(), - newRoomsAndClients, - addRoom, - addClient, - removeRoom, - removeClient, - modifyRoom, - modifyClient, - lobbyId, - moveClientToLobby, - moveClientToRoom, - clientRoomM, - clientExists, - client, - room, - client'sM, - room'sM, - allClientsM, - clientsM, - roomClientsM, - roomClientsIndicesM, - withRoomsAndClients, - allRooms, - allClients, - clientRoom, - showRooms, - roomClients - ) where - - -import Store -import Control.Monad - - -data Room r = Room { - roomClients' :: [ClientIndex], - room' :: r - } - - -data Client c = Client { - clientRoom' :: RoomIndex, - client' :: c - } - - -newtype RoomIndex = RoomIndex ElemIndex - deriving (Eq) -newtype ClientIndex = ClientIndex ElemIndex - deriving (Eq, Show, Read, Ord) - -instance Show RoomIndex where - show (RoomIndex i) = 'r' : show i - -unRoomIndex :: RoomIndex -> ElemIndex -unRoomIndex (RoomIndex r) = r - -unClientIndex :: ClientIndex -> ElemIndex -unClientIndex (ClientIndex c) = c - - -newtype MRoomsAndClients r c = MRoomsAndClients (MStore (Room r), MStore (Client c)) -newtype IRoomsAndClients r c = IRoomsAndClients (IStore (Room r), IStore (Client c)) - - -lobbyId :: RoomIndex -lobbyId = RoomIndex firstIndex - - -newRoomsAndClients :: r -> IO (MRoomsAndClients r c) -newRoomsAndClients r = do - rooms <- newStore - clients <- newStore - let rnc = MRoomsAndClients (rooms, clients) - ri <- addRoom rnc r - when (ri /= lobbyId) $ error "Empty struct inserts not at firstIndex index" - return rnc - - -roomAddClient :: ClientIndex -> Room r -> Room r -roomAddClient cl room = let cls = cl : roomClients' room; nr = room{roomClients' = cls} in cls `seq` nr `seq` nr - -roomRemoveClient :: ClientIndex -> Room r -> Room r -roomRemoveClient cl room = let cls = filter (/= cl) $ roomClients' room; nr = room{roomClients' = cls} in cls `seq` nr `seq` nr - - -addRoom :: MRoomsAndClients r c -> r -> IO RoomIndex -addRoom (MRoomsAndClients (rooms, _)) room = do - i <- addElem rooms (Room [] room) - return $ RoomIndex i - - -addClient :: MRoomsAndClients r c -> c -> IO ClientIndex -addClient (MRoomsAndClients (rooms, clients)) client = do - i <- addElem clients (Client lobbyId client) - modifyElem rooms (roomAddClient (ClientIndex i)) (unRoomIndex lobbyId) - return $ ClientIndex i - -removeRoom :: MRoomsAndClients r c -> RoomIndex -> IO () -removeRoom rnc@(MRoomsAndClients (rooms, _)) room@(RoomIndex ri) - | room == lobbyId = error "Cannot delete lobby" - | otherwise = do - clIds <- liftM roomClients' $ readElem rooms ri - forM_ clIds (moveClientToLobby rnc) - removeElem rooms ri - - -removeClient :: MRoomsAndClients r c -> ClientIndex -> IO () -removeClient (MRoomsAndClients (rooms, clients)) cl@(ClientIndex ci) = do - RoomIndex ri <- liftM clientRoom' $ readElem clients ci - modifyElem rooms (roomRemoveClient cl) ri - removeElem clients ci - - -modifyRoom :: MRoomsAndClients r c -> (r -> r) -> RoomIndex -> IO () -modifyRoom (MRoomsAndClients (rooms, _)) f (RoomIndex ri) = modifyElem rooms (\r -> r{room' = f $ room' r}) ri - -modifyClient :: MRoomsAndClients r c -> (c -> c) -> ClientIndex -> IO () -modifyClient (MRoomsAndClients (_, clients)) f (ClientIndex ci) = modifyElem clients (\c -> c{client' = f $ client' c}) ci - -moveClientInRooms :: MRoomsAndClients r c -> RoomIndex -> RoomIndex -> ClientIndex -> IO () -moveClientInRooms (MRoomsAndClients (rooms, clients)) (RoomIndex riFrom) rt@(RoomIndex riTo) cl@(ClientIndex ci) = do - modifyElem rooms (roomRemoveClient cl) riFrom - modifyElem rooms (roomAddClient cl) riTo - modifyElem clients (\c -> c{clientRoom' = rt}) ci - - -moveClientToLobby :: MRoomsAndClients r c -> ClientIndex -> IO () -moveClientToLobby rnc ci = do - room <- clientRoomM rnc ci - moveClientInRooms rnc room lobbyId ci - - -moveClientToRoom :: MRoomsAndClients r c -> RoomIndex -> ClientIndex -> IO () -moveClientToRoom rnc ri ci = moveClientInRooms rnc lobbyId ri ci - - -clientExists :: MRoomsAndClients r c -> ClientIndex -> IO Bool -clientExists (MRoomsAndClients (_, clients)) (ClientIndex ci) = elemExists clients ci - -clientRoomM :: MRoomsAndClients r c -> ClientIndex -> IO RoomIndex -clientRoomM (MRoomsAndClients (_, clients)) (ClientIndex ci) = liftM clientRoom' (clients `readElem` ci) - -client'sM :: MRoomsAndClients r c -> (c -> a) -> ClientIndex -> IO a -client'sM (MRoomsAndClients (_, clients)) f (ClientIndex ci) = liftM (f . client') (clients `readElem` ci) - -room'sM :: MRoomsAndClients r c -> (r -> a) -> RoomIndex -> IO a -room'sM (MRoomsAndClients (rooms, _)) f (RoomIndex ri) = liftM (f . room') (rooms `readElem` ri) - -allClientsM :: MRoomsAndClients r c -> IO [ClientIndex] -allClientsM (MRoomsAndClients (_, clients)) = liftM (map ClientIndex) $ indicesM clients - -clientsM :: MRoomsAndClients r c -> IO [c] -clientsM (MRoomsAndClients (_, clients)) = indicesM clients >>= mapM (\ci -> liftM client' $ readElem clients ci) - -roomClientsIndicesM :: MRoomsAndClients r c -> RoomIndex -> IO [ClientIndex] -roomClientsIndicesM (MRoomsAndClients (rooms, clients)) (RoomIndex ri) = liftM roomClients' (rooms `readElem` ri) - -roomClientsM :: MRoomsAndClients r c -> RoomIndex -> IO [c] -roomClientsM (MRoomsAndClients (rooms, clients)) (RoomIndex ri) = liftM roomClients' (rooms `readElem` ri) >>= mapM (\(ClientIndex ci) -> liftM client' $ readElem clients ci) - -withRoomsAndClients :: MRoomsAndClients r c -> (IRoomsAndClients r c -> a) -> IO a -withRoomsAndClients (MRoomsAndClients (rooms, clients)) f = - withIStore2 rooms clients (\r c -> f $ IRoomsAndClients (r, c)) - ----------------------------------------- ------------ IRoomsAndClients ----------- - -showRooms :: (Show r, Show c) => IRoomsAndClients r c -> String -showRooms rnc@(IRoomsAndClients (rooms, clients)) = concatMap showRoom (allRooms rnc) - where - showRoom r = unlines $ ((show r) ++ ": " ++ (show $ room' $ rooms ! (unRoomIndex r))) : (map showClient (roomClients' $ rooms ! (unRoomIndex r))) - showClient c = " " ++ (show c) ++ ": " ++ (show $ client' $ clients ! (unClientIndex c)) - - -allRooms :: IRoomsAndClients r c -> [RoomIndex] -allRooms (IRoomsAndClients (rooms, _)) = map RoomIndex $ indices rooms - -allClients :: IRoomsAndClients r c -> [ClientIndex] -allClients (IRoomsAndClients (_, clients)) = map ClientIndex $ indices clients - -clientRoom :: IRoomsAndClients r c -> ClientIndex -> RoomIndex -clientRoom (IRoomsAndClients (_, clients)) (ClientIndex ci) = clientRoom' (clients ! ci) - -client :: IRoomsAndClients r c -> ClientIndex -> c -client (IRoomsAndClients (_, clients)) (ClientIndex ci) = client' (clients ! ci) - -room :: IRoomsAndClients r c -> RoomIndex -> r -room (IRoomsAndClients (rooms, _)) (RoomIndex ri) = room' (rooms ! ri) - -roomClients :: IRoomsAndClients r c -> RoomIndex -> [ClientIndex] -roomClients (IRoomsAndClients (rooms, _)) (RoomIndex ri) = roomClients' $ (rooms ! ri) diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/ServerCore.hs --- a/gameServer/ServerCore.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/ServerCore.hs Thu Dec 23 17:47:50 2010 +0100 @@ -2,75 +2,69 @@ import Network import Control.Concurrent +import Control.Concurrent.STM import Control.Concurrent.Chan import Control.Monad import qualified Data.IntMap as IntMap import System.Log.Logger -import Control.Monad.Reader -import Control.Monad.State.Strict -import Data.Set as Set -import qualified Data.ByteString.Char8 as B -------------------------------------- import CoreTypes import NetRoutines +import Utils import HWProtoCore import Actions import OfficialServer.DBInteraction -import ServerState - - -timerLoop :: Int -> Chan CoreMessage -> IO () -timerLoop tick messagesChan = threadDelay (30 * 10^6) >> writeChan messagesChan (TimerAction tick) >> timerLoop (tick + 1) messagesChan -reactCmd :: [B.ByteString] -> StateT ServerState IO () -reactCmd cmd = do - (Just ci) <- gets clientIndex - rnc <- gets roomsClients - actions <- liftIO $ withRoomsAndClients rnc (\irnc -> runReader (handleCmd cmd) (ci, irnc)) - forM_ actions processAction +timerLoop :: Int -> Chan CoreMessage -> IO() +timerLoop tick messagesChan = threadDelay (30 * 10^6) >> writeChan messagesChan (TimerAction tick) >> timerLoop (tick + 1) messagesChan + +firstAway (_, a, b, c) = (a, b, c) + +reactCmd :: ServerInfo -> Int -> [String] -> Clients -> Rooms -> IO (ServerInfo, Clients, Rooms) +reactCmd serverInfo clID cmd clients rooms = + liftM firstAway $ foldM processAction (clID, serverInfo, clients, rooms) $ handleCmd clID clients rooms cmd -mainLoop :: StateT ServerState IO () -mainLoop = forever $ do - get >>= \s -> put $! s - - si <- gets serverInfo - r <- liftIO $ readChan $ coreChan si - - case r of - Accept ci -> processAction (AddClient ci) - - ClientMessage (ci, cmd) -> do - liftIO $ debugM "Clients" $ (show ci) ++ ": " ++ (show cmd) +mainLoop :: ServerInfo -> Clients -> Rooms -> IO () +mainLoop serverInfo clients rooms = do + r <- readChan $ coreChan serverInfo + + (newServerInfo, mClients, mRooms) <- + case r of + Accept ci -> + liftM firstAway $ processAction + (clientUID ci, serverInfo, clients, rooms) (AddClient ci) - removed <- gets removedClients - when (not $ ci `Set.member` removed) $ do - as <- get - put $! as{clientIndex = Just ci} - reactCmd cmd - - Remove ci -> do - liftIO $ debugM "Clients" $ "DeleteClient: " ++ show ci - processAction (DeleteClient ci) + ClientMessage (clID, cmd) -> do + debugM "Clients" $ (show clID) ++ ": " ++ (show cmd) + if clID `IntMap.member` clients then + reactCmd serverInfo clID cmd clients rooms + else + do + debugM "Clients" "Message from dead client" + return (serverInfo, clients, rooms) - --else - --do - --debugM "Clients" "Message from dead client" - --return (serverInfo, rnc) + ClientAccountInfo (clID, info) -> + if clID `IntMap.member` clients then + liftM firstAway $ processAction + (clID, serverInfo, clients, rooms) + (ProcessAccountInfo info) + else + do + debugM "Clients" "Got info for dead client" + return (serverInfo, clients, rooms) - ClientAccountInfo (ci, info) -> do - rnc <- gets roomsClients - exists <- liftIO $ clientExists rnc ci - when (exists) $ do - as <- get - put $! as{clientIndex = Just ci} - processAction (ProcessAccountInfo info) - return () + TimerAction tick -> + liftM firstAway $ + foldM processAction (0, serverInfo, clients, rooms) $ + PingAll : [StatsAction | even tick] + - TimerAction tick -> - mapM_ processAction $ - PingAll : [StatsAction | even tick] + {- let hadRooms = (not $ null rooms) && (null mrooms) + in unless ((not $ isDedicated serverInfo) && ((null clientsIn) || hadRooms)) $ + mainLoop serverInfo acceptChan messagesChan clientsIn mrooms -} + mainLoop newServerInfo mClients mRooms startServer :: ServerInfo -> Socket -> IO () startServer serverInfo serverSocket = do @@ -80,15 +74,14 @@ acceptLoop serverSocket (coreChan serverInfo) + 0 return () - - --forkIO $ timerLoop 0 $ coreChan serverInfo + + forkIO $ timerLoop 0 $ coreChan serverInfo startDBConnection serverInfo - rnc <- newRoomsAndClients newRoom + forkIO $ mainLoop serverInfo IntMap.empty (IntMap.singleton 0 newRoom) - forkIO $ evalStateT mainLoop (ServerState Nothing serverInfo Set.empty rnc) - - forever $ threadDelay (60 * 60 * 10^6) + forever $ threadDelay (60 * 60 * 10^6) >> putStrLn "***" \ No newline at end of file diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/ServerState.hs --- a/gameServer/ServerState.hs Tue Dec 14 22:32:47 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -module ServerState - ( - module RoomsAndClients, - clientRoomA, - ServerState(..), - client's, - allClientsS, - roomClientsS - ) where - -import Control.Monad.State.Strict -import Data.Set as Set ----------------------- -import RoomsAndClients -import CoreTypes - -data ServerState = ServerState { - clientIndex :: !(Maybe ClientIndex), - serverInfo :: !ServerInfo, - removedClients :: !(Set.Set ClientIndex), - roomsClients :: !MRnC - } - - -clientRoomA :: StateT ServerState IO RoomIndex -clientRoomA = do - (Just ci) <- gets clientIndex - rnc <- gets roomsClients - liftIO $ clientRoomM rnc ci - -client's :: (ClientInfo -> a) -> StateT ServerState IO a -client's f = do - (Just ci) <- gets clientIndex - rnc <- gets roomsClients - liftIO $ client'sM rnc f ci - -allClientsS :: StateT ServerState IO [ClientInfo] -allClientsS = gets roomsClients >>= liftIO . clientsM - -roomClientsS :: RoomIndex -> StateT ServerState IO [ClientInfo] -roomClientsS ri = do - rnc <- gets roomsClients - liftIO $ roomClientsM rnc ri diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/Store.hs --- a/gameServer/Store.hs Tue Dec 14 22:32:47 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +0,0 @@ -module Store( - ElemIndex(), - MStore(), - IStore(), - newStore, - addElem, - removeElem, - readElem, - writeElem, - modifyElem, - elemExists, - firstIndex, - indicesM, - withIStore, - withIStore2, - (!), - indices - ) where - -import qualified Data.Array.IArray as IA -import qualified Data.Array.IO as IOA -import qualified Data.IntSet as IntSet -import Data.IORef -import Control.Monad - - -newtype ElemIndex = ElemIndex Int - deriving (Eq, Show, Read, Ord) -newtype MStore e = MStore (IORef (IntSet.IntSet, IntSet.IntSet, IOA.IOArray Int e)) -newtype IStore e = IStore (IntSet.IntSet, IA.Array Int e) - - -firstIndex :: ElemIndex -firstIndex = ElemIndex 0 - --- MStore code -initialSize :: Int -initialSize = 10 - - -growFunc :: Int -> Int -growFunc a = a * 3 `div` 2 - - -newStore :: IO (MStore e) -newStore = do - newar <- IOA.newArray_ (0, initialSize - 1) - new <- newIORef (IntSet.empty, IntSet.fromAscList [0..initialSize - 1], newar) - return (MStore new) - - -growStore :: MStore e -> IO () -growStore (MStore ref) = do - (busyElems, freeElems, arr) <- readIORef ref - (_, m') <- IOA.getBounds arr - let newM' = growFunc (m' + 1) - 1 - newArr <- IOA.newArray_ (0, newM') - sequence_ [IOA.readArray arr i >>= IOA.writeArray newArr i | i <- [0..m']] - writeIORef ref (busyElems, freeElems `IntSet.union` (IntSet.fromAscList [m'+1..newM']), newArr) - - -growIfNeeded :: MStore e -> IO () -growIfNeeded m@(MStore ref) = do - (_, freeElems, _) <- readIORef ref - when (IntSet.null freeElems) $ growStore m - - -addElem :: MStore e -> e -> IO ElemIndex -addElem m@(MStore ref) element = do - growIfNeeded m - (busyElems, freeElems, arr) <- readIORef ref - let (n, freeElems') = IntSet.deleteFindMin freeElems - IOA.writeArray arr n element - writeIORef ref (IntSet.insert n busyElems, freeElems', arr) - return $ ElemIndex n - - -removeElem :: MStore e -> ElemIndex -> IO () -removeElem (MStore ref) (ElemIndex n) = do - (busyElems, freeElems, arr) <- readIORef ref - IOA.writeArray arr n (error $ "Store: no element " ++ show n) - writeIORef ref (IntSet.delete n busyElems, IntSet.insert n freeElems, arr) - - -readElem :: MStore e -> ElemIndex -> IO e -readElem (MStore ref) (ElemIndex n) = readIORef ref >>= \(_, _, arr) -> IOA.readArray arr n - - -writeElem :: MStore e -> ElemIndex -> e -> IO () -writeElem (MStore ref) (ElemIndex n) el = readIORef ref >>= \(_, _, arr) -> IOA.writeArray arr n el - - -modifyElem :: MStore e -> (e -> e) -> ElemIndex -> IO () -modifyElem (MStore ref) f (ElemIndex n) = do - (_, _, arr) <- readIORef ref - IOA.readArray arr n >>= IOA.writeArray arr n . f - -elemExists :: MStore e -> ElemIndex -> IO Bool -elemExists (MStore ref) (ElemIndex n) = do - (_, free, _) <- readIORef ref - return $ n `IntSet.notMember` free - -indicesM :: MStore e -> IO [ElemIndex] -indicesM (MStore ref) = do - (busy, _, _) <- readIORef ref - return $ map ElemIndex $ IntSet.toList busy - - --- A way to see MStore elements in pure code via IStore -m2i :: MStore e -> IO (IStore e) -m2i (MStore ref) = do - (a, _, c') <- readIORef ref - c <- IOA.unsafeFreeze c' - return $ IStore (a, c) - -i2m :: (MStore e) -> IStore e -> IO () -i2m (MStore ref) (IStore (_, arr)) = do - (b, e, _) <- readIORef ref - a <- IOA.unsafeThaw arr - writeIORef ref (b, e, a) - -withIStore :: MStore e -> (IStore e -> a) -> IO a -withIStore m f = do - i <- m2i m - let res = f i - res `seq` i2m m i - return res - - -withIStore2 :: MStore e1 -> MStore e2 -> (IStore e1 -> IStore e2 -> a) -> IO a -withIStore2 m1 m2 f = do - i1 <- m2i m1 - i2 <- m2i m2 - let res = f i1 i2 - res `seq` i2m m1 i1 - i2m m2 i2 - return res - - --- IStore code -(!) :: IStore e -> ElemIndex -> e -(!) (IStore (_, arr)) (ElemIndex i) = (IA.!) arr i - -indices :: IStore e -> [ElemIndex] -indices (IStore (busy, _)) = map ElemIndex $ IntSet.toList busy diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/Utils.hs --- a/gameServer/Utils.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/Utils.hs Thu Dec 23 17:47:50 2010 +0100 @@ -1,4 +1,3 @@ -{-# LANGUAGE OverloadedStrings #-} module Utils where import Control.Concurrent @@ -17,34 +16,37 @@ import Data.Maybe ------------------------------------------------- import qualified Codec.Binary.Base64 as Base64 -import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString as BW +import qualified Data.ByteString.UTF8 as BUTF8 +import qualified Data.ByteString as B import CoreTypes -sockAddr2String :: SockAddr -> IO B.ByteString -sockAddr2String (SockAddrInet _ hostAddr) = liftM B.pack $ inet_ntoa hostAddr +sockAddr2String :: SockAddr -> IO String +sockAddr2String (SockAddrInet _ hostAddr) = inet_ntoa hostAddr sockAddr2String (SockAddrInet6 _ _ (a, b, c, d) _) = - return $ B.pack $ (foldr1 (.) + return $ (foldr1 (.) $ List.intersperse (\a -> ':':a) $ concatMap (\n -> (\(a, b) -> [showHex a, showHex b]) $ divMod n 65536) [a, b, c, d]) [] -toEngineMsg :: B.ByteString -> B.ByteString -toEngineMsg msg = B.pack $ Base64.encode (fromIntegral (BW.length msg) : (BW.unpack msg)) +toEngineMsg :: String -> String +toEngineMsg msg = Base64.encode (fromIntegral (B.length encodedMsg) : (B.unpack encodedMsg)) + where + encodedMsg = BUTF8.fromString msg -fromEngineMsg :: B.ByteString -> Maybe B.ByteString -fromEngineMsg msg = Base64.decode (B.unpack msg) >>= removeLength >>= return . BW.pack +fromEngineMsg :: String -> Maybe String +fromEngineMsg msg = liftM (map w2c) (Base64.decode msg >>= removeLength) where removeLength (x:xs) = if length xs == fromIntegral x then Just xs else Nothing removeLength _ = Nothing -checkNetCmd :: B.ByteString -> (Bool, Bool) -checkNetCmd = check . liftM B.unpack . fromEngineMsg +checkNetCmd :: String -> (Bool, Bool) +checkNetCmd msg = check decoded where + decoded = fromEngineMsg msg check Nothing = (False, False) check (Just (m:ms)) = (m `Set.member` legalMessages, m == '+') check _ = (False, False) - legalMessages = Set.fromList $ "M#+LlRrUuDdZzAaSjJ,sFNpPwtghb12345" ++ slotMessages + legalMessages = Set.fromList $ "M#+LlRrUuDdZzAaSjJ,sFNpPwtghbc12345" ++ slotMessages slotMessages = "\128\129\130\131\132\133\134\135\136\137\138" maybeRead :: Read a => String -> Maybe a @@ -52,17 +54,29 @@ [(x, rest)] | all isSpace rest -> Just x _ -> Nothing -teamToNet :: TeamInfo -> [B.ByteString] -teamToNet team = - "ADD_TEAM" - : teamname team - : teamgrave team - : teamfort team - : teamvoicepack team - : teamflag team - : teamowner team - : (B.pack $ show $ difficulty team) - : hhsInfo +teamToNet :: Word16 -> TeamInfo -> [String] +teamToNet protocol team + | protocol < 30 = [ + "ADD_TEAM", + teamname team, + teamgrave team, + teamfort team, + teamvoicepack team, + teamowner team, + show $ difficulty team + ] + ++ hhsInfo + | otherwise = [ + "ADD_TEAM", + teamname team, + teamgrave team, + teamfort team, + teamvoicepack team, + teamflag team, + teamowner team, + show $ difficulty team + ] + ++ hhsInfo where hhsInfo = concatMap (\(HedgehogInfo name hat) -> [name, hat]) $ hedgehogs team @@ -76,10 +90,10 @@ else t : replaceTeam team teams -illegalName :: B.ByteString -> Bool -illegalName = all isSpace . B.unpack +illegalName :: String -> Bool +illegalName = all isSpace -protoNumber2ver :: Word16 -> B.ByteString +protoNumber2ver :: Word16 -> String protoNumber2ver 17 = "0.9.7-dev" protoNumber2ver 19 = "0.9.7" protoNumber2ver 20 = "0.9.8-dev" @@ -105,13 +119,3 @@ putStr msg hFlush stdout getLine - - -unfoldrE :: (b -> Either b (a, b)) -> b -> ([a], b) -unfoldrE f b = - case f b of - Right (a, new_b) -> let (a', b') = unfoldrE f new_b in (a : a', b') - Left new_b -> ([], new_b) - -showB :: Show a => a -> B.ByteString -showB = B.pack .show diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/hedgewars-server.cabal --- a/gameServer/hedgewars-server.cabal Tue Dec 14 22:32:47 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -Name: hedgewars-server -Version: 0.1 -Synopsis: hedgewars server -Description: hedgewars server -Homepage: http://www.hedgewars.org/ -License: GPL-2 -Author: unC0Rr -Maintainer: unC0Rr@hedgewars.org -Category: Game -Build-type: Simple -Cabal-version: >=1.2 - - -Executable hedgewars-server - main-is: hedgewars-server.hs - - Build-depends: - base >= 4, - unix, - containers, - array, - bytestring, - network-bytestring, - network, - time, - stm, - mtl, - dataenc, - hslogger, - process - - ghc-options: -O2 \ No newline at end of file diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/hedgewars-server.hs --- a/gameServer/hedgewars-server.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/hedgewars-server.hs Thu Dec 23 17:47:50 2010 +0100 @@ -2,15 +2,23 @@ module Main where -import Network +import Network.Socket +import qualified Network +import Network.BSD import Control.Concurrent.STM import Control.Concurrent.Chan +#if defined(NEW_EXCEPTIONS) +import qualified Control.OldException as Exception +#else import qualified Control.Exception as Exception +#endif import System.Log.Logger ----------------------------------- import Opts import CoreTypes +import OfficialServer.DBInteraction import ServerCore +import Utils #if !defined(mingw32_HOST_OS) @@ -18,12 +26,10 @@ #endif -setupLoggers :: IO () setupLoggers = updateGlobalLogger "Clients" (setLevel INFO) -main :: IO () main = withSocketsDo $ do #if !defined(mingw32_HOST_OS) installHandler sigPIPE Ignore Nothing; @@ -32,11 +38,11 @@ setupLoggers - stats' <- atomically $ newTMVar (StatisticsInfo 0 0) + stats <- atomically $ newTMVar (StatisticsInfo 0 0) dbQueriesChan <- newChan - coreChan' <- newChan - serverInfo' <- getOpts $ newServerInfo stats' coreChan' dbQueriesChan - + coreChan <- newChan + serverInfo' <- getOpts $ newServerInfo stats coreChan dbQueriesChan + #if defined(OFFICIAL_SERVER) dbHost' <- askFromConsole "DB host: " dbLogin' <- askFromConsole "login: " @@ -46,7 +52,14 @@ let serverInfo = serverInfo' #endif + + proto <- getProtocolNumber "tcp" Exception.bracket - (Network.listenOn $ Network.PortNumber $ listenPort serverInfo) + (socket AF_INET Stream proto) sClose - (startServer serverInfo) + (\sock -> do + setSocketOption sock ReuseAddr 1 + bindSocket sock (SockAddrInet (listenPort serverInfo) iNADDR_ANY) + listen sock maxListenQueue + startServer serverInfo sock + ) diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/stresstest.hs --- a/gameServer/stresstest.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/stresstest.hs Thu Dec 23 17:47:50 2010 +0100 @@ -6,7 +6,7 @@ import System.IO import Control.Concurrent import Network -import Control.OldException +import Control.Exception import Control.Monad import System.Random @@ -14,24 +14,24 @@ import System.Posix #endif -session1 nick room = ["NICK", nick, "", "PROTO", "32", "", "PING", "", "CHAT", "lobby 1", "", "CREATE_ROOM", room, "", "CHAT", "room 1", "", "QUIT", "creator", ""] -session2 nick room = ["NICK", nick, "", "PROTO", "32", "", "LIST", "", "JOIN_ROOM", room, "", "CHAT", "room 2", "", "PART", "", "CHAT", "lobby after part", "", "QUIT", "part-quit", ""] -session3 nick room = ["NICK", nick, "", "PROTO", "32", "", "LIST", "", "JOIN_ROON", room, "", "CHAT", "room 2", "", "QUIT", "quit", ""] +session1 nick room = ["NICK", nick, "", "PROTO", "24", "", "CHAT", "lobby 1", "", "CREATE", room, "", "CHAT", "room 1", "", "QUIT", "bye-bye", ""] +session2 nick room = ["NICK", nick, "", "PROTO", "24", "", "LIST", "", "JOIN", room, "", "CHAT", "room 2", "", "PART", "", "CHAT", "lobby after part", "", "QUIT", "bye-bye", ""] +session3 nick room = ["NICK", nick, "", "PROTO", "24", "", "LIST", "", "JOIN", room, "", "CHAT", "room 2", "", "QUIT", "bye-bye", ""] emulateSession sock s = do - mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (30000::Int, 59000) >>= threadDelay) s + mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (50000::Int, 90000) >>= threadDelay) s hFlush sock threadDelay 225000 -testing = Control.OldException.handle print $ do +testing = Control.Exception.handle print $ do putStrLn "Start" sock <- connectTo "127.0.0.1" (PortNumber 46631) num1 <- randomRIO (70000::Int, 70100) num2 <- randomRIO (0::Int, 2) num3 <- randomRIO (0::Int, 5) - let nick1 = 'n' : show num1 - let room1 = 'r' : show num2 + let nick1 = show num1 + let room1 = show num2 case num2 of 0 -> emulateSession sock $ session1 nick1 room1 1 -> emulateSession sock $ session2 nick1 room1 @@ -40,7 +40,7 @@ putStrLn "Finish" forks = forever $ do - delay <- randomRIO (30000::Int, 59000) + delay <- randomRIO (10000::Int, 19000) threadDelay delay forkIO testing diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/stresstest2.hs --- a/gameServer/stresstest2.hs Tue Dec 14 22:32:47 2010 +0100 +++ b/gameServer/stresstest2.hs Thu Dec 23 17:47:50 2010 +0100 @@ -6,7 +6,7 @@ import System.IO import Control.Concurrent import Network -import Control.OldException +import Control.Exception import Control.Monad import System.Random @@ -14,28 +14,22 @@ import System.Posix #endif -session1 nick room = ["NICK", nick, "", "PROTO", "32", ""] - - - -testing = Control.OldException.handle print $ do - putStrLn "Start" +testing = Control.Exception.handle print $ do + delay <- randomRIO (100::Int, 300) + threadDelay delay sock <- connectTo "127.0.0.1" (PortNumber 46631) + hClose sock - num1 <- randomRIO (70000::Int, 70100) - num2 <- randomRIO (0::Int, 2) - num3 <- randomRIO (0::Int, 5) - let nick1 = 'n' : show num1 - let room1 = 'r' : show num2 - mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (300::Int, 590) >>= threadDelay) $ session1 nick1 room1 - mapM_ (\x -> hPutStrLn sock x >> hFlush sock) $ concatMap (\x -> ["CHAT_MSG", show x, ""]) [1..] - hClose sock - putStrLn "Finish" - -forks = testing +forks i = do + delay <- randomRIO (50::Int, 190) + if i `mod` 10 == 0 then putStr (show i) else putStr "." + hFlush stdout + threadDelay delay + forkIO testing + forks (i + 1) main = withSocketsDo $ do #if !defined(mingw32_HOST_OS) installHandler sigPIPE Ignore Nothing; #endif - forks + forks 1 diff -r 467ab0685890 -r 5ef5415c4ee1 gameServer/stresstest3.hs --- a/gameServer/stresstest3.hs Tue Dec 14 22:32:47 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -{-# LANGUAGE CPP #-} - -module Main where - -import IO -import System.IO -import Control.Concurrent -import Network -import Control.OldException -import Control.Monad -import System.Random -import Control.Monad.State -import Data.List - -#if !defined(mingw32_HOST_OS) -import System.Posix -#endif - -type SState = Handle -io = liftIO - -readPacket :: StateT SState IO [String] -readPacket = do - h <- get - p <- io $ hGetPacket h [] - return p - where - hGetPacket h buf = do - l <- hGetLine h - if (not $ null l) then hGetPacket h (buf ++ [l]) else return buf - -waitPacket :: String -> StateT SState IO Bool -waitPacket s = do - p <- readPacket - return $ head p == s - -sendPacket :: [String] -> StateT SState IO () -sendPacket s = do - h <- get - io $ do - mapM_ (hPutStrLn h) s - hPutStrLn h "" - hFlush h - -emulateSession :: StateT SState IO () -emulateSession = do - n <- io $ randomRIO (100000::Int, 100100) - waitPacket "CONNECTED" - sendPacket ["NICK", "test" ++ (show n)] - waitPacket "NICK" - sendPacket ["PROTO", "31"] - waitPacket "PROTO" - b <- waitPacket "LOBBY:JOINED" - --io $ print b - sendPacket ["QUIT", "BYE"] - return () - -testing = Control.OldException.handle print $ do - putStr "+" - sock <- connectTo "127.0.0.1" (PortNumber 46631) - evalStateT emulateSession sock - --hClose sock - putStr "-" - hFlush stdout - -forks = forM_ [1..100] $ const $ do - delay <- randomRIO (10000::Int, 30000) - threadDelay delay - forkIO testing - -main = withSocketsDo $ do -#if !defined(mingw32_HOST_OS) - installHandler sigPIPE Ignore Nothing; -#endif - forks diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/GSHandlers.inc --- a/hedgewars/GSHandlers.inc Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/GSHandlers.inc Thu Dec 23 17:47:50 2010 +0100 @@ -28,32 +28,32 @@ // Gear is still on the same Pixel it was before if steps < 1 then - begin + begin if onlyCheckIfChanged then - begin + begin Gear^.X := Gear^.X + dX; Gear^.Y := Gear^.Y + dY; EXIT; - end + end else steps := 1; - end; + end; if steps > 1 then - begin + begin sX:= dX / steps; sY:= dY / steps; - end + end else - begin + begin sX:= dX; sY:= dY; - end; + end; caller:= Gear^.doStep; for i:= 1 to steps do - begin + begin Gear^.X := Gear^.X + sX; Gear^.Y := Gear^.Y + sY; step(Gear); @@ -61,7 +61,7 @@ or ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then break; - end; + end; end; procedure makeHogsWorry(x, y: hwFloat; r: LongInt); @@ -71,27 +71,27 @@ begin gi := GearsList; while gi <> nil do - begin + begin if (gi^.Kind = gtHedgehog) then - begin + begin d := r - hwRound(Distance(gi^.X - x, gi^.Y - y)); if (d > 1) and not gi^.Invulnerable and (GetRandom(2) = 0) then - begin + begin if (CurrentHedgehog^.Gear = gi) then PlaySound(sndOops, gi^.Hedgehog^.Team^.voicepack) else - begin + begin if (gi^.State and gstMoving) = 0 then gi^.State := gi^.State or gstLoser; if d > r div 2 then PlaySound(sndNooo, gi^.Hedgehog^.Team^.voicepack) else PlaySound(sndUhOh, gi^.Hedgehog^.Team^.voicepack); + end; end; end; + gi := gi^.NextGear end; - gi := gi^.NextGear - end; end; //////////////////////////////////////////////////////////////////////////////// procedure doStepDrowningGear(Gear: PGear); @@ -107,28 +107,29 @@ isSubmersible:= (Gear = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.AmmoType = amJetpack); // probably needs tweaking. might need to be in a case statement based upon gear type if cWaterLine < hwRound(Gear^.Y) + Gear^.Radius then - begin + begin skipSpeed := _0_25; skipAngle := _1_9; skipDecay := _0_87; // this could perhaps be a tiny bit higher. if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed) and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)) then - begin + begin Gear^.dY.isNegative := true; Gear^.dY := Gear^.dY * skipDecay; Gear^.dX := Gear^.dX * skipDecay; CheckGearDrowning := false; PlaySound(sndSkip) - end + end else - begin + begin if not isSubmersible then - begin + begin CheckGearDrowning := true; Gear^.State := gstDrowning; Gear^.RenderTimer := false; - if (Gear^.Kind <> gtSniperRifleShot) and (Gear^.Kind <> gtShotgunShot) and (Gear^.Kind <> gtDEagleShot) and (Gear^.Kind <> gtSineGunShot) then + if (Gear^.Kind <> gtSniperRifleShot) and (Gear^.Kind <> gtShotgunShot) and + (Gear^.Kind <> gtDEagleShot) and (Gear^.Kind <> gtSineGunShot) then if Gear^.Kind = gtHedgehog then begin if Gear^.Hedgehog^.Effects[heResurrectable] then @@ -152,21 +153,21 @@ if ((cReducedQuality and rqPlainSplash) = 0) and (((not isSubmersible) and (hwRound(Gear^.Y) < cWaterLine + 64 + Gear^.Radius)) or (isSubmersible and (hwRound(Gear^.Y) < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0) and (CurAmmoGear^.dY < _0_01)))) then - begin + begin AddVisualGear(hwRound(Gear^.X), cWaterLine, vgtSplash); maxDrops := (Gear^.Radius div 2) + hwRound(Gear^.dX * Gear^.Radius * 2) + hwRound(Gear^. dY * Gear^.Radius * 2); for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do - begin + begin particle := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), cWaterLine, vgtDroplet); if particle <> nil then - begin + begin particle^.dX := particle^.dX - (Gear^.dX.QWordValue / 42949672960); particle^.dY := particle^.dY - (Gear^.dY.QWordValue / 21474836480) + end end - end - end; + end; if isSubmersible and (CurAmmoGear^.Pos = 0) then CurAmmoGear^.Pos := 1000 end else @@ -188,17 +189,16 @@ particle: PVisualGear; begin if _0_4 < Gear^.dY then - begin + begin dmg := ModifyDamage(1 + hwRound((hwAbs(Gear^.dY) - _0_4) * 70), Gear); PlaySound(sndBump); if dmg < 1 then exit; for i:= min(12, (3 + dmg div 10)) downto 0 do - begin - particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, - vgtDust); + begin + particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480); - end; + end; if (Gear^.Invulnerable) then exit; @@ -267,53 +267,53 @@ if (hwRound(Gear^.X) < LAND_WIDTH div -2) or (hwRound(Gear^.X) > LAND_WIDTH * 3 div 2) then Gear^.State := Gear^.State or gstCollision; if Gear^.dY.isNegative then - begin + begin isFalling := true; if TestCollisionYwithGear(Gear, -1) then - begin + begin collV := -1; Gear^.dX := Gear^.dX * Gear^.Friction; Gear^.dY := - Gear^.dY * Gear^.Elasticity; Gear^.State := Gear^.State or gstCollision - end + end else if (Gear^.AdvBounce=1) and TestCollisionYwithGear(Gear, 1) then collV := 1; - end + end else if TestCollisionYwithGear(Gear, 1) then begin - collV := 1; - isFalling := false; - Gear^.dX := Gear^.dX * Gear^.Friction; - Gear^.dY := - Gear^.dY * Gear^.Elasticity; - Gear^.State := Gear^.State or gstCollision + collV := 1; + isFalling := false; + Gear^.dX := Gear^.dX * Gear^.Friction; + Gear^.dY := - Gear^.dY * Gear^.Elasticity; + Gear^.State := Gear^.State or gstCollision end else - begin + begin isFalling := true; if (Gear^.AdvBounce=1) and not Gear^.dY.isNegative and TestCollisionYwithGear(Gear, -1) then collV := -1; - end; + end; if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then - begin + begin collH := hwSign(Gear^.dX); Gear^.dX := - Gear^.dX * Gear^.Elasticity; Gear^.dY := Gear^.dY * Gear^.Elasticity; Gear^.State := Gear^.State or gstCollision - end + end else if (Gear^.AdvBounce=1) and TestCollisionXwithGear(Gear, -hwSign(Gear^.dX)) then collH := -hwSign(Gear^.dX); //if Gear^.AdvBounce and (collV <>0) and (collH <> 0) and (hwSqr(tdX) + hwSqr(tdY) > _0_08) then if (Gear^.AdvBounce=1) and (collV <>0) and (collH <> 0) and ((collV=-1) or ((tdX.QWordValue + tdY.QWordValue) > _0_2.QWordValue)) then - begin + begin Gear^.dX := tdY*Gear^.Elasticity*Gear^.Friction; Gear^.dY := tdX*Gear^.Elasticity; //*Gear^.Friction; Gear^.dY.isNegative := not tdY.isNegative; isFalling := false; Gear^.AdvBounce := 10; - end; + end; if Gear^.AdvBounce > 1 then dec(Gear^.AdvBounce); @@ -519,16 +519,46 @@ if (GameFlags and gfMoreWind) = 0 then Gear^.dX := Gear^.dX + cWindSpeed; doStepFallingGear(Gear); if (Gear^.State and gstCollision) <> 0 then - begin + begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); DeleteGear(Gear); exit - end; + end; if (GameTicks and $3F) = 0 then AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); end; //////////////////////////////////////////////////////////////////////////////// +procedure doStepSnowball(Gear: PGear); +var kick, i: LongInt; + particle: PVisualGear; +begin + AllInactive := false; + if (GameFlags and gfMoreWind) = 0 then Gear^.dX := Gear^.dX + cWindSpeed; + doStepFallingGear(Gear); + CalcRotationDirAngle(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + kick:= hwRound((hwAbs(Gear^.dX)+hwAbs(Gear^.dY)) * _20); + Gear^.dY.isNegative:= not Gear^.dY.isNegative; + Gear^.dX.isNegative:= not Gear^.dX.isNegative; + AmmoShove(Gear, 1, kick); + for i:= 15 + kick div 10 downto 0 do + begin + particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust); + if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) + end; + DeleteGear(Gear); + exit + end; + if ((GameTicks and $1F) = 0) and (Random(3) = 0) then + begin + particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust); + if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) + end +end; + +//////////////////////////////////////////////////////////////////////////////// procedure doStepGrave(Gear: PGear); begin AllInactive := false; @@ -895,7 +925,7 @@ //////////////////////////////////////////////////////////////////////////////// procedure doStepPickHammerWork(Gear: PGear); var - i, ei: LongInt; + i, ei, x, y: LongInt; HHGear: PGear; begin AllInactive := false; @@ -903,53 +933,58 @@ dec(Gear^.Timer); if (Gear^.Timer = 0)or((Gear^.Message and gmDestroy) <> 0)or((HHGear^.State and gstHHDriven) = 0) then - begin + begin StopSound(Gear^.SoundChannel); DeleteGear(Gear); AfterAttack; doStepHedgehogMoving(HHGear); // for gfInfAttack exit - end; - + end; + + x:= hwRound(Gear^.X); + y:= hwRound(Gear^.Y); if (Gear^.Timer mod 33) = 0 then - begin + begin HHGear^.State := HHGear^.State or gstNoDamage; - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y) + 7, 6, EXPLDontDraw); + doMakeExplosion(x, y + 7, 6, EXPLDontDraw); HHGear^.State := HHGear^.State and not gstNoDamage - end; + end; if (Gear^.Timer mod 47) = 0 then - begin - for i:= 0 to 1 do - AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); - i := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2)); - ei := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2)); + begin + // ok. this was an attempt to turn off dust if not actually drilling land. I have no idea why it isn't working as expected + //if ((y + 12 and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then + for i:= 0 to 1 do + AddVisualGear(x - 5 + Random(10), y + 12, vgtDust); + + i := x - Gear^.Radius - LongInt(GetRandom(2)); + ei := x + Gear^.Radius + LongInt(GetRandom(2)); while i <= ei do - begin - DrawExplosion(i, hwRound(Gear^.Y) + 3, 3); + begin + DrawExplosion(i, y + 3, 3); inc(i, 1) - end; + end; if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9) , lfIndestructible) then - begin + begin Gear^.X := Gear^.X + Gear^.dX; Gear^.Y := Gear^.Y + _1_9; - end; + end; SetAllHHToActive; - end; + end; if TestCollisionYwithGear(Gear, 1) then - begin + begin Gear^.dY := _0; SetLittle(HHGear^.dX); HHGear^.dY := _0; - end + end else - begin + begin Gear^.dY := Gear^.dY + cGravity; Gear^.Y := Gear^.Y + Gear^.dY; if hwRound(Gear^.Y) > cWaterLine then Gear^.Timer := 1 - end; + end; Gear^.X := Gear^.X + HHGear^.dX; HHGear^.X := Gear^.X; @@ -1858,10 +1893,10 @@ DeleteCI(HHGear); for i:= 0 to 3 do - begin + begin AmmoShove(Gear, 30, 25); Gear^.X := Gear^.X + Gear^.dX * 5 - end; + end; HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving; @@ -2688,7 +2723,7 @@ if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then if (Land[y, x] <> 0) then - begin + begin Gear^.dX.isNegative := not Gear^.dX.isNegative; Gear^.dY.isNegative := not Gear^.dY.isNegative; Gear^.dX := Gear^.dX * _1_5; @@ -2696,13 +2731,13 @@ AmmoShove(Gear, 0, 40); AfterAttack; DeleteGear(Gear) - end + end + else else - else - begin + begin AfterAttack; DeleteGear(Gear) - end + end end; procedure doStepSeductionWear(Gear: PGear); diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/HHHandlers.inc --- a/hedgewars/HHHandlers.inc Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/HHHandlers.inc Thu Dec 23 17:47:50 2010 +0100 @@ -207,6 +207,7 @@ amClusterBomb: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtClusterBomb, 0, newDx, newDy, CurWeapon^.Timer); amGasBomb: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtGasBomb, 0, newDx, newDy, CurWeapon^.Timer); amBazooka: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtShell, 0, newDx, newDy, 0); + amSnowball: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtSnowball, 0, newDx, newDy, 0); amBee: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtBee, 0, newDx, newDy, 0); amShotgun: begin PlaySound(sndShotgunReload); diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/uAIAmmoTests.pas --- a/hedgewars/uAIAmmoTests.pas Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/uAIAmmoTests.pas Thu Dec 23 17:47:50 2010 +0100 @@ -31,6 +31,7 @@ end; function TestBazooka(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; +function TestSnowball(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestGrenade(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestMolotov(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; function TestClusterBomb(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; @@ -102,7 +103,8 @@ (proc: @TestGrenade; flags: 0), // amSMine (proc: @TestFirePunch; flags: 0), // amHammer (proc: nil; flags: 0), // amResurrector - (proc: nil; flags: 0) // amDrillStrike + (proc: nil; flags: 0),// amDrillStrike + (proc: @TestSnowball; flags: 0) // amSnowball ); const BadTurn = Low(LongInt) div 4; @@ -172,6 +174,63 @@ TestBazooka:= valueResult end; +function TestSnowball(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; +var Vx, Vy, r: hwFloat; + rTime: LongInt; + Score, EX, EY: LongInt; + valueResult: LongInt; + + function CheckTrace: LongInt; + var x, y, dX, dY: hwFloat; + t: LongInt; + value: LongInt; + begin + x:= Me^.X; + y:= Me^.Y; + dX:= Vx; + dY:= -Vy; + t:= rTime; + repeat + x:= x + dX; + y:= y + dY; + dX:= dX + cWindSpeed; + dY:= dY + cGravity; + dec(t) + until TestCollExcludingMe(Me, hwRound(x), hwRound(y), 5) or (t <= 0); + EX:= hwRound(x); + EY:= hwRound(y); + value:= RateExplosion(Me, EX, EY, 5); + if value = 0 then value:= - Metric(Targ.X, Targ.Y, EX, EY) div 64; + CheckTrace:= value; + end; + +begin +ap.Time:= 0; +rTime:= 350; +ap.ExplR:= 0; +valueResult:= BadTurn; +repeat + rTime:= rTime + 300 + Level * 50 + random(300); + Vx:= - cWindSpeed * rTime * _0_5 + (int2hwFloat(Targ.X + AIrndSign(2)) - Me^.X) / int2hwFloat(rTime); + Vy:= cGravity * rTime * _0_5 - (int2hwFloat(Targ.Y) - Me^.Y) / int2hwFloat(rTime); + r:= Distance(Vx, Vy); + if not (r > _1) then + begin + Score:= CheckTrace; + if valueResult <= Score then + begin + ap.Angle:= DxDy2AttackAngle(Vx, Vy) + AIrndSign(random((Level - 1) * 9)); + ap.Power:= hwRound(r * cMaxPower) - random((Level - 1) * 17 + 1); + ap.ExplR:= 100; + ap.ExplX:= EX; + ap.ExplY:= EY; + valueResult:= Score + end; + end +until (rTime > 4250); +TestSnowball:= valueResult +end; + function TestMolotov(Me: PGear; Targ: TPoint; Level: LongInt; var ap: TAttackParams): LongInt; var Vx, Vy, r: hwFloat; Score, EX, EY, valueResult: LongInt; diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/uCommandHandlers.pas --- a/hedgewars/uCommandHandlers.pas Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/uCommandHandlers.pas Thu Dec 23 17:47:50 2010 +0100 @@ -9,6 +9,17 @@ implementation uses uCommands, uTypes, uVariables, uIO, uDebug, uConsts, uScript, uUtils, SDLh, uRandom; +procedure chGenCmd(var s: shortstring); +begin +case s[1] of + 'R': if ReadyTimeLeft > 1 then + begin + ReadyTimeLeft:= 1; + if not CurrentTeam^.ExtDriven then SendIPC('c'+s); + end + end +end; + procedure chQuit(var s: shortstring); const prevGState: TGameState = gsConfirm; begin @@ -152,7 +163,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('L'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmLeft and InputMask) @@ -172,7 +182,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('R'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmRight and InputMask) @@ -192,7 +201,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('U'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmUp and InputMask) @@ -212,7 +220,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('D'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmDown and InputMask) @@ -232,7 +239,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('Z'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmPrecise and InputMask); @@ -252,7 +258,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('j'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmLJump and InputMask) @@ -263,7 +268,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('J'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmHJump and InputMask) @@ -273,7 +277,6 @@ begin s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do begin @@ -304,7 +307,6 @@ s:= s; // avoid compiler hint if CheckNoTeamOrHH or isPaused then exit; if not CurrentTeam^.ExtDriven then SendIPC('S'); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do Message:= Message or (gmSwitch and InputMask) @@ -326,7 +328,6 @@ if (s[0] <> #1) or (s[1] < '1') or (s[1] > '5') or CheckNoTeamOrHH then exit; if not CurrentTeam^.ExtDriven then SendIPC(s); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do begin @@ -342,7 +343,6 @@ slot:= byte(s[1]) - 49; if slot > cMaxSlotIndex then exit; if not CurrentTeam^.ExtDriven then SendIPC(char(byte(s[1]) + 79)); -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; bShowFinger:= false; with CurrentHedgehog^.Gear^ do begin @@ -438,7 +438,6 @@ ((MultiShootAttacks > 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) = 0)) or ((Gear^.State and gstHHDriven) = 0) then else bShowAmmoMenu:= true end; - if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1 end end; @@ -465,7 +464,6 @@ procedure chPause(var s: shortstring); begin s:= s; // avoid compiler hint -if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1; if gameType <> gmtNet then isPaused:= not isPaused; SDL_ShowCursor(ord(isPaused)) @@ -512,6 +510,24 @@ procedure initModule; begin +//////// Begin top sorted by freq analysis not including chatmsg + RegisterVariable('+right' , vtCommand, @chRight_p , false); + RegisterVariable('-right' , vtCommand, @chRight_m , false); + RegisterVariable('+up' , vtCommand, @chUp_p , false); + RegisterVariable('-up' , vtCommand, @chUp_m , false); + RegisterVariable('+left' , vtCommand, @chLeft_p , false); + RegisterVariable('-left' , vtCommand, @chLeft_m , false); + RegisterVariable('+attack' , vtCommand, @chAttack_p , false); + RegisterVariable('+down' , vtCommand, @chDown_p , false); + RegisterVariable('-down' , vtCommand, @chDown_m , false); + RegisterVariable('hjump' , vtCommand, @chHJump , false); + RegisterVariable('ljump' , vtCommand, @chLJump , false); + RegisterVariable('nextturn', vtCommand, @chNextTurn , false); + RegisterVariable('-attack' , vtCommand, @chAttack_m , false); + RegisterVariable('slot' , vtCommand, @chSlot , false); + RegisterVariable('setweap' , vtCommand, @chSetWeapon , false); +//////// End top by freq analysis + RegisterVariable('gencmd' , vtCommand, @chGenCmd , false); RegisterVariable('flag' , vtCommand, @chFlag , false); RegisterVariable('script' , vtCommand, @chScript , false); RegisterVariable('proto' , vtCommand, @chCheckProto , true ); @@ -555,25 +571,10 @@ RegisterVariable('ammomenu', vtCommand, @chAmmoMenu , true); RegisterVariable('+precise', vtCommand, @chPrecise_p , false); RegisterVariable('-precise', vtCommand, @chPrecise_m , false); - RegisterVariable('+left' , vtCommand, @chLeft_p , false); - RegisterVariable('-left' , vtCommand, @chLeft_m , false); - RegisterVariable('+right' , vtCommand, @chRight_p , false); - RegisterVariable('-right' , vtCommand, @chRight_m , false); - RegisterVariable('+up' , vtCommand, @chUp_p , false); - RegisterVariable('-up' , vtCommand, @chUp_m , false); - RegisterVariable('+down' , vtCommand, @chDown_p , false); - RegisterVariable('-down' , vtCommand, @chDown_m , false); - RegisterVariable('+attack' , vtCommand, @chAttack_p , false); - RegisterVariable('-attack' , vtCommand, @chAttack_m , false); RegisterVariable('switch' , vtCommand, @chSwitch , false); - RegisterVariable('nextturn', vtCommand, @chNextTurn , false); RegisterVariable('timer' , vtCommand, @chTimer , false); RegisterVariable('taunt' , vtCommand, @chTaunt , false); - RegisterVariable('setweap' , vtCommand, @chSetWeapon , false); - RegisterVariable('slot' , vtCommand, @chSlot , false); RegisterVariable('put' , vtCommand, @chPut , false); - RegisterVariable('ljump' , vtCommand, @chLJump , false); - RegisterVariable('hjump' , vtCommand, @chHJump , false); RegisterVariable('+volup' , vtCommand, @chVol_p , true ); RegisterVariable('-volup' , vtCommand, @chVol_m , true ); RegisterVariable('+voldown', vtCommand, @chVol_m , true ); diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/uCommands.pas --- a/hedgewars/uCommands.pas Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/uCommands.pas Thu Dec 23 17:47:50 2010 +0100 @@ -56,11 +56,11 @@ begin //WriteLnToConsole(CmdStr); if CmdStr[0]=#0 then exit; -{$IFDEF DEBUGFILE}AddFileLog('ParseCommand "' + CmdStr + '"');{$ENDIF} c:= CmdStr[1]; if c in ['/', '$'] then Delete(CmdStr, 1, 1) else c:= '/'; s:= ''; SplitBySpace(CmdStr, s); +{$IFDEF DEBUGFILE}AddFileLog('[Cmd] ' + c + CmdStr + ' (' + inttostr(length(CmdStr)) + ')');{$ENDIF} t:= Variables; while t <> nil do begin diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/uConsole.pas --- a/hedgewars/uConsole.pas Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/uConsole.pas Thu Dec 23 17:47:50 2010 +0100 @@ -31,7 +31,7 @@ uses Types, uVariables, uUtils; const cLineWidth: LongInt = 0; - cLinesCount = 256; + cLinesCount = 8; type TTextLine = record @@ -52,7 +52,7 @@ done: boolean; begin {$IFNDEF NOCONSOLE} -{$IFDEF DEBUGFILE}AddFileLog('Console write: ' + s);{$ENDIF} +{$IFDEF DEBUGFILE}AddFileLog('[Con] ' + s);{$ENDIF} Write(s); done:= false; diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/uGears.pas --- a/hedgewars/uGears.pas Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/uGears.pas Thu Dec 23 17:47:50 2010 +0100 @@ -127,7 +127,8 @@ @doStepHammer, @doStepHammerHit, @doStepResurrector, - @doStepNapalmBomb + @doStepNapalmBomb, + @doStepSnowball ); procedure InsertGearToList(Gear: PGear); @@ -246,6 +247,11 @@ gtShell: begin gear^.Radius:= 4; end; + gtSnowball: begin + gear^.Radius:= 4; + gear^.Elasticity:= _1; + gear^.Friction:= _1; + end; gtGrave: begin gear^.ImpactSound:= sndGraveImpact; gear^.nImpactSounds:= 1; @@ -1245,13 +1251,10 @@ begin if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then - begin + begin VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit); - if VGear <> nil then - begin - VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); + if VGear <> nil then VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); end; - end; if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then Gear^.FlightTime:= 1; diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/uGearsRender.pas --- a/hedgewars/uGearsRender.pas Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/uGearsRender.pas Thu Dec 23 17:47:50 2010 +0100 @@ -543,6 +543,7 @@ CurWeapon:= GetAmmoEntry(HH^); case amt of amBazooka: DrawRotated(sprHandBazooka, hx, hy, sign, aangle); + amSnowball: DrawRotated(sprHandSnowball, hx, hy, sign, aangle); amMortar: DrawRotated(sprHandMortar, hx, hy, sign, aangle); amMolotov: DrawRotated(sprHandMolotov, hx, hy, sign, aangle); amBallgun: DrawRotated(sprHandBallgun, hx, hy, sign, aangle); @@ -845,6 +846,7 @@ begin case Gear^.Kind of gtBomb: DrawRotated(sprBomb, x, y, 0, Gear^.DirAngle); + gtSnowball: DrawRotated(sprSnowball, x, y, 0, Gear^.DirAngle); gtGasBomb: DrawRotated(sprCheese, x, y, 0, Gear^.DirAngle); gtMolotov: DrawRotated(sprMolotov, x, y, 0, Gear^.DirAngle); diff -r 467ab0685890 -r 5ef5415c4ee1 hedgewars/uIO.pas --- a/hedgewars/uIO.pas Tue Dec 14 22:32:47 2010 +0100 +++ b/hedgewars/uIO.pas Thu Dec 23 17:47:50 2010 +0100 @@ -139,7 +139,7 @@ else loTicks:= SDLNet_Read16(@s[byte(s[0]) - 1]); AddCmd(loTicks, s); - {$IFDEF DEBUGFILE}AddFileLog('IPC in: '+s[1]+' ticks '+IntToStr(lastcmd^.loTime));{$ENDIF} + {$IFDEF DEBUGFILE}AddFileLog('[IPC in] '+s[1]+' ticks '+IntToStr(lastcmd^.loTime));{$ENDIF} end end; @@ -220,7 +220,7 @@ SendEmptyPacketTicks:= 0; if s[0]>#251 then s[0]:= #251; SDLNet_Write16(GameTicks, @s[Succ(byte(s[0]))]); - {$IFDEF DEBUGFILE}AddFileLog('IPC send: '+ s[1]);{$ENDIF} + {$IFDEF DEBUGFILE}AddFileLog('[IPC out] '+ s[1]);{$ENDIF} inc(s[0], 2); SDLNet_TCP_Send(IPCSock, @s, Succ(byte(s[0]))) end @@ -247,7 +247,7 @@ procedure SendIPCTimeInc; const timeinc: shortstring = '#'; begin -{$IFDEF DEBUGFILE}AddFileLog('IPC Send #');{$ENDIF} +{$IFDEF DEBUGFILE}AddFileLog('[IPC out]