Updated Slovak translation + merge
authorjose1711
Thu, 23 Dec 2010 17:47:50 +0100
changeset 4588 5ef5415c4ee1
parent 4529 467ab0685890 (current diff)
parent 4586 4ba4f021070f (diff)
child 4647 20b982afbe6e
Updated Slovak translation + merge
gameServer/HandlerUtils.hs
gameServer/RoomsAndClients.hs
gameServer/ServerState.hs
gameServer/Store.hs
gameServer/hedgewars-server.cabal
gameServer/stresstest3.hs
share/hedgewars/Data/Locale/sk.txt
share/hedgewars/Data/Scripts/Multiplayer/RandomWeapon.cfg
share/hedgewars/Data/Scripts/Multiplayer/RandomWeapon.lua
--- 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
--- 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()) {
--- 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<QVariant> 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();
 }
 
--- 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);
--- 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;
--- 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<QPoint> points = paths[0];
+    if(!paths.size()) return;
+
+    QList<QPoint> points = paths.at(0);
 
     QPoint prevPoint = points.first();
     int i = 1;
--- 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:
--- 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 <QFile>
+#include <QMessageBox>
+
 #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())));
+    }
+}
--- 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
--- 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<QVariant> 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()));
 }
--- 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;
--- 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();
 }
 
--- 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[] = {
--- 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();
 
--- 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:
--- 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 <string>
-
 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;
--- 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;
--- 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) {
--- 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 <QVBoxLayout>
 #include <QIcon>
 #include <QLineEdit>
+#include <QMessageBox>
 
 #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 <QMessageBox>
+
 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);
+    }
+}
--- 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
--- 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()
--- 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 <QDataWidgetMapper>
 #include <QTime>
 #include <QSlider>
+#include <QFileDialog>
 
 #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);
+}
--- 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
--- 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());
 }
 
--- 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);
 
--- 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)
--- 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}")
 
--- 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
--- 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]
--- 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
--- 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)"]
--- 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)"]
--- 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)"]
--- 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
--- 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
--- 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 =
--- 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)
--- 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
--- 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)
--- 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
--- 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
--- 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
--- 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
--- 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
--- 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
+        )
--- 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
 
--- 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
--- 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
--- 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);
--- 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);
--- 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;
--- 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 );
--- 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
--- 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;
 
--- 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;
 
--- 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);
 
--- 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] <time increment>');{$ENDIF}
 SendIPCRaw(@timeinc, 2)
 end;
 
@@ -309,6 +309,10 @@
         'j': ParseCommand('ljump', true);
         'J': ParseCommand('hjump', true);
         ',': ParseCommand('skip', true);
+        'c': begin
+            s:= copy(headcmd^.str, 2, Pred(headcmd^.len));
+            ParseCommand('gencmd ' + s, true);
+            end;
         's': begin
             s:= copy(headcmd^.str, 2, Pred(headcmd^.len));
             ParseCommand('chatmsg ' + s, true);
@@ -371,7 +375,6 @@
 procedure doPut(putX, putY: LongInt; fromAI: boolean);
 begin
 if CheckNoTeamOrHH or isPaused then exit;
-if ReadyTimeLeft > 1 then ReadyTimeLeft:= 1;
 bShowFinger:= false;
 if not CurrentTeam^.ExtDriven and bShowAmmoMenu then
     begin
--- a/hedgewars/uKeys.pas	Tue Dec 14 22:32:47 2010 +0100
+++ b/hedgewars/uKeys.pas	Thu Dec 23 17:47:50 2010 +0100
@@ -171,14 +171,19 @@
 if CurrentBinds[i][0] <> #0 then
     begin
     if (i > 3) and (tkbdn[i] <> 0) and not ((CurrentBinds[i] = 'put') or (CurrentBinds[i] = 'ammomenu') or (CurrentBinds[i] = '+cur_u') or (CurrentBinds[i] = '+cur_d') or (CurrentBinds[i] = '+cur_l') or (CurrentBinds[i] = '+cur_r')) then hideAmmoMenu:= true;
-    if (tkbd[i] = 0) and (tkbdn[i] <> 0) then ParseCommand(CurrentBinds[i], Trusted)
+    if (tkbd[i] = 0) and (tkbdn[i] <> 0) then
+         begin
+         ParseCommand(CurrentBinds[i], Trusted);
+         if (CurrentTeam <> nil) and not CurrentTeam^.ExtDriven and (ReadyTimeLeft > 1) then ParseCommand('gencmd R', true)
+         end
     else if (CurrentBinds[i][1] = '+')
             and (tkbdn[i] = 0)
             and (tkbd[i] <> 0) then
             begin
             s:= CurrentBinds[i];
             s[1]:= '-';
-            ParseCommand(s, Trusted)
+            ParseCommand(s, Trusted);
+            if (CurrentTeam <> nil) and not CurrentTeam^.ExtDriven and (ReadyTimeLeft > 1) then ParseCommand('gencmd R', true)
             end;
     tkbd[i]:= tkbdn[i]
     end
--- a/hedgewars/uLand.pas	Tue Dec 14 22:32:47 2010 +0100
+++ b/hedgewars/uLand.pas	Thu Dec 23 17:47:50 2010 +0100
@@ -553,7 +553,7 @@
 begin
     uLandPainted.Draw;
 
-    MaxHedgehogs:= 18;
+    MaxHedgehogs:= 48;
     hasGirders:= true;
     playHeight:= 2048;
     playWidth:= 4096;
@@ -1045,6 +1045,8 @@
         0: GenBlank(EdgeTemplates[SelectTemplate]);
         1: GenMaze;
         2: GenDrawnMap;
+    else
+        OutError('Unknown mapgen', true);
     end;
     AddProgress();
 
@@ -1286,6 +1288,8 @@
         0: GenBlank(EdgeTemplates[SelectTemplate]);
         1: GenMaze;
         2: GenDrawnMap;
+    else
+        OutError('Unknown mapgen', true);
     end;
 
     lh:= LAND_HEIGHT div 128;
--- a/hedgewars/uMisc.pas	Tue Dec 14 22:32:47 2010 +0100
+++ b/hedgewars/uMisc.pas	Thu Dec 23 17:47:50 2010 +0100
@@ -122,11 +122,11 @@
 var convertedSurf: PSDL_Surface = nil;
 begin
     if (tmpsurf^.format^.bitsperpixel = 24) or ((tmpsurf^.format^.bitsperpixel = 32) and (tmpsurf^.format^.rshift > tmpsurf^.format^.bshift)) then
-    begin
+        begin
         convertedSurf:= SDL_ConvertSurface(tmpsurf, @conversionFormat, SDL_SWSURFACE);
         SDL_FreeSurface(tmpsurf);
         exit(convertedSurf);
-    end;
+        end;
 
     exit(tmpsurf);
 end;
--- a/hedgewars/uScript.pas	Tue Dec 14 22:32:47 2010 +0100
+++ b/hedgewars/uScript.pas	Thu Dec 23 17:47:50 2010 +0100
@@ -381,7 +381,7 @@
             vg^.dY:= lua_tonumber(L, 5);
             vg^.Angle:= lua_tonumber(L, 6);
             vg^.Frame:= lua_tointeger(L, 7);
-            vg^.FrameTicks:= lua_tointeger(L, 8);
+            if lua_tointeger(L, 8) <> 0 then vg^.FrameTicks:= lua_tointeger(L, 8);  // find a better way to do this. maybe need to break all these up.
             vg^.State:= lua_tointeger(L, 9);
             vg^.Timer:= lua_tointeger(L, 10);
             vg^.Tint:= lua_tointeger(L, 11);
@@ -674,17 +674,17 @@
 function lc_hogsay(L : Plua_State) : LongInt; Cdecl;
 var gear : PGear;
    vgear : PVisualGear;
+       s : LongWord;
 begin
-    if lua_gettop(L) <> 3 then
-        begin
-        LuaError('Lua: Wrong number of parameters passed to HogSay!');
-        end
-    else
+    if lua_gettop(L) = 4 then s:= lua_tointeger(L, 4)
+    else s:= 0;
+
+    if (lua_gettop(L) = 4) or (lua_gettop(L) = 3) then
         begin
         gear:= GearByUID(lua_tointeger(L, 1));
         if gear <> nil then
             begin
-            vgear:= AddVisualGear(0, 0, vgtSpeechBubble);
+            vgear:= AddVisualGear(0, 0, vgtSpeechBubble, s, true);
             if vgear <> nil then
                begin
                vgear^.Text:= lua_tostring(L, 2);
@@ -693,7 +693,8 @@
                if (vgear^.FrameTicks < 1) or (vgear^.FrameTicks > 3) then vgear^.FrameTicks:= 1;
                end;
             end
-        end;
+        end
+    else LuaError('Lua: Wrong number of parameters passed to HogSay!');
     lc_hogsay:= 0
 end;
 
--- a/hedgewars/uTypes.pas	Tue Dec 14 22:32:47 2010 +0100
+++ b/hedgewars/uTypes.pas	Thu Dec 23 17:47:50 2010 +0100
@@ -52,7 +52,7 @@
             sprCheese, sprHandCheese, sprHandFlamethrower, sprChunk, sprNote,
             sprSMineOff, sprSMineOn, sprHandSMine, sprHammer,
             sprHandResurrector, sprCross, sprAirDrill, sprNapalmBomb,
-            sprBulletHit
+            sprBulletHit, sprSnowball, sprHandSnowball
             );
 
     // Gears that interact with other Gears and/or Land
@@ -67,7 +67,7 @@
             gtSniperRifleShot, gtJetpack, gtMolotov, gtExplosives, gtBirdy, // 45
             gtEgg, gtPortal, gtPiano, gtGasBomb, gtSineGunShot, gtFlamethrower, // 51
             gtSMine, gtPoisonCloud, gtHammer, gtHammerHit, gtResurrector, // 56
-            gtNapalmBomb); // 57
+            gtNapalmBomb, gtSnowball); // 58
 
     // Gears that are _only_ of visual nature (e.g. background stuff, visual effects, speechbubbles, etc.)
     TVisualGearType = (vgtFlake, vgtCloud, vgtExplPart, vgtExplPart2, vgtFire,
@@ -112,7 +112,7 @@
             amRCPlane, amLowGravity, amExtraDamage, amInvulnerable, amExtraTime, // 35
             amLaserSight, amVampiric, amSniperRifle, amJetpack, amMolotov, amBirdy, amPortalGun, // 42
             amPiano, amGasBomb, amSineGun, amFlamethrower, amSMine, amHammer, // 48
-            amResurrector, amDrillStrike);
+            amResurrector, amDrillStrike, amSnowball);
 
     TCrateType = (HealthCrate, AmmoCrate, UtilityCrate);
 
@@ -342,7 +342,7 @@
             sidLowGravity, sidExtraDamage, sidInvulnerable, sidExtraTime,
             sidLaserSight, sidVampiric, sidSniperRifle, sidJetpack,
             sidMolotov, sidBirdy, sidPortalGun, sidPiano, sidGasBomb, sidSineGun, sidFlamethrower,
-            sidSMine, sidHammer, sidResurrector, sidDrillStrike);
+            sidSMine, sidHammer, sidResurrector, sidDrillStrike, sidSnowball);
 
     TMsgStrId = (sidStartFight, sidDraw, sidWinner, sidVolume, sidPaused,
             sidConfirm, sidSuddenDeath, sidRemaining, sidFuel, sidSync,
--- a/hedgewars/uVariables.pas	Tue Dec 14 22:32:47 2010 +0100
+++ b/hedgewars/uVariables.pas	Thu Dec 23 17:47:50 2010 +0100
@@ -23,7 +23,7 @@
     cReducedQuality : LongWord    = rqNone;
     //userNick is in uChat
     recordFileName  : shortstring = '';
-    cReadyDelay     : Longword    = 0;
+    cReadyDelay     : Longword    = 5000;
     cLogfileBase    : shortstring = 'debug';
 //////////////////////////
 
@@ -548,8 +548,12 @@
             (FileName:  'BulletHit'; Path: ptGraphics; AltPath: ptNone;
                 Texture: nil; Surface: nil; Width:  32; Height: 32;
                 imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
-                tpMedium; getDimensions: false; getImageDimensions: true)
+                tpMedium; getDimensions: false; getImageDimensions: true),
             // sprNapalmBomb
+            (FileName:  'Snowball'; Path: ptCurrTheme; AltPath: ptGraphics; Texture: nil; Surface: nil;
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSnowball
+            (FileName:  'amSnowball'; Path: ptCurrTheme; AltPath: ptHedgehog; Texture: nil; Surface: nil;
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true) // sprHandSnowball
             );
 
 
@@ -1957,11 +1961,32 @@
             PosCount: 2;
             PosSprite: sprAmAirplane;
             ejectX: 0;
+            ejectY: 0),
+// Snowball
+            (NameId: sidSnowball;
+            NameTex: nil;
+            Probability: 0;
+            NumberInCase: 1;
+            Ammo: (Propz: ammoprop_Power or ammoprop_AltUse;
+                Count: 2;
+                NumPerTurn: 0;
+                Timer: 0;
+                Pos: 0;
+                AmmoType: amSnowball;
+                AttackVoice: sndNone);
+            Slot: 0;
+            TimeAfterTurn: 3000;
+            minAngle: 0;
+            maxAngle: 0;
+            isDamaging: true;
+            SkipTurns: 0;
+            PosCount: 1;
+            PosSprite: sprWater;
+            ejectX: 0; 
             ejectY: 0)
         );
 
 
-
     conversionFormat: TSDL_PixelFormat = (
         palette: nil;
         BitsPerPixel : 32;
@@ -2248,7 +2273,7 @@
     cReducedQuality := rqNone;
     //userNick is in uChat
     recordFileName  := '';
-    cReadyDelay     := 0;
+    cReadyDelay     := 5000;
 end;
 
 end.
--- a/hedgewars/uVisualGears.pas	Tue Dec 14 22:32:47 2010 +0100
+++ b/hedgewars/uVisualGears.pas	Thu Dec 23 17:47:50 2010 +0100
@@ -374,6 +374,7 @@
 procedure DrawVisualGears(Layer: LongWord);
 var Gear: PVisualGear;
     tinted: boolean;
+    tmp: real;
 begin
 Gear:= VisualGearsList;
 case Layer of
@@ -497,7 +498,12 @@
             vgtSmallDamageTag: DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex);
             vgtSpeechBubble: if (Gear^.Tex <> nil) and (((Gear^.State = 0) and (Gear^.Hedgehog^.Team = CurrentTeam)) or (Gear^.State = 2)) then DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex);
             vgtHealthTag: if Gear^.Tex <> nil then DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex);
-            vgtCircle: DrawCircle(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State, Gear^.Timer);
+            vgtCircle:  if gear^.Angle = 1 then 
+                            begin
+                            tmp:= Gear^.State / 100;
+                            DrawTexture(round(Gear^.X-24*tmp) + WorldDx, round(Gear^.Y-24*tmp) + WorldDy, SpritesData[sprVampiric].Texture, tmp)
+                            end
+                        else DrawCircle(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State, Gear^.Timer);
         end;
         if (Gear^.Tint <> $FFFFFFFF) or tinted then Tint($FF,$FF,$FF,$FF);
         Gear:= Gear^.NextGear
--- a/project_files/HedgewarsMobile/Classes/CommodityFunctions.h	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/CommodityFunctions.h	Thu Dec 23 17:47:50 2010 +0100
@@ -58,7 +58,7 @@
 #define IS_DUALHEAD()           ([[UIScreen class] respondsToSelector:@selector(screens)] && [[UIScreen screens] count] > 1)
 #define IS_IPAD()               (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
 #define IS_NOT_POWERFUL()       ([modelType() hasPrefix:@"iPhone1"] || [modelType() hasPrefix:@"iPod1,1"] || [modelType() hasPrefix:@"iPod2,1"])
-#define IS_NOT_VERY_POWERFUL()  ([modelType() hasPrefix:@"iPad1"]   || [modelType() hasPrefix:@"iPhone2"] || [modelType() hasPrefix:@"iPod3"]  )
+#define IS_NOT_VERY_POWERFUL()  ([modelType() hasPrefix:@"iPad1"] || [modelType() hasPrefix:@"iPhone2"] || [modelType() hasPrefix:@"iPod3"] || [modelType() hasPrefix:@"iPod4"] )
 
 #define DEFAULT_NETGAME_PORT    46631
 
--- a/project_files/HedgewarsMobile/Classes/GameSetup.h	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/GameSetup.h	Thu Dec 23 17:47:50 2010 +0100
@@ -42,9 +42,8 @@
 
 -(id) initWithDictionary:(NSDictionary *)gameDictionary;
 -(void) engineProtocol;
--(void) startThread:(NSString *)selector;
--(int)  sendToEngine:(NSString *)string;
--(int)  sendToEngineNoSave:(NSString *)string;
+-(int) sendToEngine:(NSString *)string;
+-(int) sendToEngineNoSave:(NSString *)string;
 -(void) provideTeamData:(NSString *)teamName forHogs:(NSInteger) numberOfPlayingHogs withHealth:(NSInteger) initialHealth ofColor:(NSNumber *)teamColor;
 -(void) provideAmmoData:(NSString *)ammostoreName forPlayingTeams:(NSInteger) numberOfTeams;
 -(NSInteger) provideScheme:(NSString *)schemeName;
--- a/project_files/HedgewarsMobile/Classes/GameSetup.m	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/GameSetup.m	Thu Dec 23 17:47:50 2010 +0100
@@ -21,7 +21,6 @@
 
 #import "GameSetup.h"
 #import "SDL_uikitappdelegate.h"
-#import "SDL_net.h"
 #import "PascalImports.h"
 #import "CommodityFunctions.h"
 #import "OverlayViewController.h"
@@ -216,13 +215,7 @@
 }
 
 #pragma mark -
-#pragma mark Thread/Network relevant code
-// select one of GameSetup method and execute it in a seprate thread
--(void) startThread:(NSString *)selector {
-    SEL usage = NSSelectorFromString(selector);
-    [NSThread detachNewThreadSelector:usage toTarget:self withObject:nil];
-}
-
+#pragma mark Network relevant code
 -(void) dumpRawData:(const uint8_t*)buffer ofSize:(uint8_t) length {
     // is it performant to reopen the stream every time?
     NSOutputStream *os = [[NSOutputStream alloc] initToFileAtPath:self.savePath append:YES];
@@ -250,148 +243,6 @@
     return SDLNet_TCP_Send(csd, [string UTF8String], length);
 }
 
--(int) sendToServer:(NSString *)command withArgument:(NSString *)argument {
-    NSString *message = [[NSString alloc] initWithFormat:@"%@\n%@\n\n",command,argument];
-    int result = SDLNet_TCP_Send(esd, [message UTF8String], [message length]);
-    [message release];
-    return result;
-}
-
--(int) sendToServer:(NSString *)command {
-    NSString *message = [[NSString alloc] initWithFormat:@"%@\n\n",command];
-    int result = SDLNet_TCP_Send(esd, [message UTF8String], [message length]);
-    [message release];
-    return result;
-}
-
--(void) serverProtocol {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    IPaddress ip;
-    BOOL clientQuit = NO;
-    char *buffer = (char *)malloc(sizeof(char)*BUFFER_SIZE);
-    int dim = BUFFER_SIZE;
-    uint8_t msgSize;
-    NSString *arg = nil;
-
-    if (SDLNet_Init() < 0) {
-        DLog(@"SDLNet_Init: %s", SDLNet_GetError());
-        clientQuit = YES;
-    }
-
-    // Resolving the host using NULL make network interface to listen
-    if (SDLNet_ResolveHost(&ip, "netserver.hedgewars.org", DEFAULT_NETGAME_PORT) < 0 && !clientQuit) {
-        DLog(@"SDLNet_ResolveHost: %s\n", SDLNet_GetError());
-        clientQuit = YES;
-    }
-
-    // Open a connection with the IP provided (listen on the host's port)
-    if (!(esd = SDLNet_TCP_Open(&ip)) && !clientQuit) {
-        DLog(@"SDLNet_TCP_Open: %s %\n", SDLNet_GetError(), DEFAULT_NETGAME_PORT);
-        clientQuit = YES;
-    }
-
-    DLog(@"Found server on port %d", DEFAULT_NETGAME_PORT);
-    while (!clientQuit) {
-        int index = 0;
-        BOOL exitBufferLoop = NO;
-        memset(buffer, '\0', dim);
-        
-        while (exitBufferLoop != YES) {
-            msgSize = SDLNet_TCP_Recv(esd, &buffer[index], 2);
-            
-            // exit in case of error
-            if (msgSize <= 0) {
-                DLog(@"SDLNet_TCP_Recv: %s", SDLNet_GetError());
-                clientQuit = YES;
-                break;
-            }
-            
-            // update index position and check for End-Of-Message
-            index += msgSize;
-            if (strncmp(&buffer[index-2], "\n\n", 2) == 0) {
-                exitBufferLoop = YES;
-            }
-            
-            // if message is too big allocate new space
-            if (index >= dim) {
-                dim += BUFFER_SIZE;
-                buffer = (char *)realloc(buffer, dim);
-                if (buffer == NULL) {
-                    clientQuit = YES;
-                    break;
-                }
-            }
-        }
-
-        NSString *bufferedMessage = [[NSString alloc] initWithBytes:buffer length:index-2 encoding:NSASCIIStringEncoding];
-        NSArray *listOfCommands = [bufferedMessage componentsSeparatedByString:@"\n"];
-        [bufferedMessage release];
-        NSString *command = [listOfCommands objectAtIndex:0];
-        DLog(@"size = %d, %@", index-2, listOfCommands);
-        if ([command isEqualToString:@"PING"]) {
-            if ([listOfCommands count] > 1)
-                [self sendToServer:@"PONG" withArgument:[listOfCommands objectAtIndex:1]];
-            else
-                [self sendToServer:@"PONG"];
-
-            [arg release];
-        }
-        else if ([command isEqualToString:@"NICK"]) {
-            //TODO: what is this for?
-        }
-        else if ([command isEqualToString:@"PROTO"]) {
-            if ([[listOfCommands objectAtIndex:1] intValue] == 34) {
-                //TODO: unused
-            }
-        }
-        else if ([command isEqualToString:@"ROOM"]) {
-            //TODO: stub
-        }
-        else if ([command isEqualToString:@"LOBBY:LEFT"]) {
-            //TODO: stub
-        }
-        else if ([command isEqualToString:@"LOBBY:JOINED"]) {
-            //TODO: stub
-        }
-        else if ([command isEqualToString:@"ASKPASSWORD"]) {
-            //TODO: store hashed password in settings.plist (nil here will vouluntary trigger an exception)
-            [self sendToServer:@"PASSWORD" withArgument:nil];
-        }
-        else if ([command isEqualToString:@"CONNECTED"]) {
-            [self sendToServer:@"NICK" withArgument:@"koda"];
-            [self sendToServer:@"PROTO" withArgument:@"34"];
-        }
-        else if ([command isEqualToString:@"SERVER_MESSAGE"]) {
-            DLog(@"%@", [listOfCommands objectAtIndex:1]);
-        }
-        else if ([command isEqualToString:@"WARNING"]) {
-            if ([listOfCommands count] > 1)
-                DLog(@"Server warning - %@", [listOfCommands objectAtIndex:1]);
-            else
-                DLog(@"Server warning - unknown");
-        }
-        else if ([command isEqualToString:@"ERROR"]) {
-            DLog(@"Server error - %@", [listOfCommands objectAtIndex:1]);
-        }
-        else if ([command isEqualToString:@"BYE"]) {
-            //TODO: handle "Reconnected too fast"
-            DLog(@"Server disconnected, reason: %@", [listOfCommands objectAtIndex:1]);
-            clientQuit = YES;
-        }
-        else {
-            DLog(@"Unknown/Unsupported message received: %@", command);
-        }
-    }
-    DLog(@"Server exited, ending thread");
-
-    free(buffer);
-    // Close the client socket
-    SDLNet_TCP_Close(esd);
-    SDLNet_Quit();
-
-    [pool release];
-}
-
 // method that handles net setup with engine and keeps connection alive
 -(void) engineProtocol {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@@ -494,7 +345,7 @@
                 [self dumpRawData:buffer ofSize:msgSize];
 
                 sscanf((char *)buffer, "%*s %d", &eProto);
-                short int netProto = 0;
+                short int netProto;
                 char *versionStr;
 
                 HW_versionInfo(&netProto, &versionStr);
@@ -510,11 +361,35 @@
                     case 'r':
                         DLog(@"Winning team: %s", &buffer[2]);
                         break;
+                    case 'D':
+                        DLog(@"Best Shot: %s", &buffer[2]);
+                        break;
                     case 'k':
                         DLog(@"Best Hedgehog: %s", &buffer[2]);
                         break;
+                    case 'K':
+                        DLog(@"Hogs Killed: %s", &buffer[2]);
+                        break;
+                    case 'H':
+                        //something about team health
+                        break;
+                    case 'T':
+                        // local team stats
+                        break;
+                    case 'P':
+                        // player postion
+                        break;
+                    case 's':
+                        DLog(@"Most self damage: %s", &buffer[2]);
+                        break;
+                    case 'S':
+                        DLog(@"Most friendly fire: %s", &buffer[2]);
+                        break;
+                    case 'B':
+                        DLog(@"Most turn skipped by: %s", &buffer[2]);
+                        break;
                     default:
-                        // TODO: losta stats stuff
+                        DLog(@"Unhandled stat message, see statsPage.cpp");
                         break;
                 }
                 break;
@@ -570,19 +445,19 @@
     NSString *verticalSize = [[NSString alloc] initWithFormat:@"%d", height];
     const char **gameArgs = (const char**) malloc(sizeof(char *) * 10);
     NSInteger tmpQuality;
+    BOOL enhanced = [[self.systemSettings objectForKey:@"enhanced"] boolValue];
 
     NSString *modelId = modelType();
-    if ([modelId hasPrefix:@"iPhone1"] ||                                   // = iPhone or iPhone 3G
-        [modelId hasPrefix:@"iPod1,1"] || [modelId hasPrefix:@"iPod2,1"])   // = iPod Touch or iPod Touch 2G
-        tmpQuality = 0x00000001 | 0x00000002 | 0x00000008 | 0x00000040;  // rqLowRes | rqBlurryLand | rqSimpleRope | rqKillFlakes
-    else if ([modelId hasPrefix:@"iPhone2"] ||                              // = iPhone 3GS
-             [modelId hasPrefix:@"iPod3"])                                  // = iPod Touch 3G
-            tmpQuality = 0x00000002 | 0x00000040;           // rqBlurryLand | rqKillFlakes
-        else if ([modelId hasPrefix:@"iPad1"])                              // = iPad
-                tmpQuality = 0x00000002;                    // rqBlurryLand
-            else                                                            // = everything else
-                tmpQuality = 0;                             // full quality
-    if (IS_IPAD() == NO)             // = disable tooltips on phone
+    if ([modelId hasPrefix:@"iPhone1"] || [modelId hasPrefix:@"iPod1,1"] || [modelId hasPrefix:@"iPod2,1"])     // = iPhone and iPhone 3G or iPod Touch or iPod Touch 2G
+        tmpQuality = 0x00000001 | 0x00000002 | 0x00000008 | 0x00000040;                 // rqLowRes | rqBlurryLand | rqSimpleRope | rqKillFlakes
+    else if ([modelId hasPrefix:@"iPhone2"] || [modelId hasPrefix:@"iPod3"])                                    // = iPhone 3GS or iPod Touch 3G
+        tmpQuality = 0x00000002 | 0x00000040;                                           // rqBlurryLand | rqKillFlakes
+    else if ([modelId hasPrefix:@"iPad1"] || [modelId hasPrefix:@"iPod4"] || enhanced == NO)                    // = iPad 1G or iPod Touch 4G or not enhanced mode
+        tmpQuality = 0x00000002;                                                        // rqBlurryLand
+    else                                                                                                        // = everything else
+        tmpQuality = 0;                                                                 // full quality
+
+    if (IS_IPAD() == NO)                                                                                        // = disable tooltips on phone
         tmpQuality = tmpQuality | 0x00000400;
 
     // prevents using an empty nickname
--- a/project_files/HedgewarsMobile/Classes/GeneralSettingsViewController.m	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/GeneralSettingsViewController.m	Thu Dec 23 17:47:50 2010 +0100
@@ -76,7 +76,13 @@
         case 30:    //alternateSwitch
             [self.settingsDictionary setObject:[NSNumber numberWithBool:theSwitch.on] forKey:@"alternate"];
             break;
-        case 60:    //getReady
+        case 70:    //enhanced graphics
+            [self.settingsDictionary setObject:[NSNumber numberWithBool:theSwitch.on] forKey:@"enhanced"];
+            break;
+        case 80:    //nomultitasking
+            [self.settingsDictionary setObject:[NSNumber numberWithBool:theSwitch.on] forKey:@"multitasking"];
+            break;
+        case 60:    //classic menu
             [self.settingsDictionary setObject:[NSNumber numberWithBool:theSwitch.on] forKey:@"menu"];
             break;
         default:
@@ -88,8 +94,10 @@
 -(void) saveTextFieldValue:(NSString *)textString withTag:(NSInteger) tagValue {
     if (tagValue == 40)
         [self.settingsDictionary setObject:textString forKey:@"username"];
-    else
-        [self.settingsDictionary setObject:textString forKey:@"password"];
+    else {
+        [self.settingsDictionary setObject:[NSNumber numberWithInt:[textString length]] forKey:@"password_length"];
+        [self.settingsDictionary setObject:[textString MD5hash] forKey:@"password"];
+    }
 }
 
 #pragma mark -
@@ -98,7 +106,7 @@
     return 3;
 }
 
--(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger )section {
     switch (section) {
         case 0:     // user and pass
             return 1;   // set 2 here for the password field
@@ -107,10 +115,10 @@
             return 2;
             break;
         case 2:     // other stuff
-            if (IS_IPAD())
-                return 2;
+            if (IS_IPAD() == YES)
+                return 4;
             else
-                return 1;
+                return 3;
             break;
         default:
             DLog(@"Nope");
@@ -166,11 +174,19 @@
                 editableCell.textField.secureTextEntry = NO;
                 editableCell.tag = 40;
             } else {
+                // create a dummy password for setting some visual content for the password
+                int pwdLength = [[self.settingsDictionary objectForKey:@"password_length"] intValue];
+                char *dummyStr = (char *)malloc(sizeof(char)*pwdLength);
+                for (int i = 0; i < pwdLength; i++)
+                    dummyStr[i] = i;
+                NSString *dummy = [[NSString alloc] initWithBytes:dummyStr length:pwdLength encoding:NSASCIIStringEncoding];
+                free(dummyStr);
                 editableCell.titleLabel.text = NSLocalizedString(@"Password","from the settings table");
                 editableCell.textField.placeholder = NSLocalizedString(@"Insert your password",@"");
-                editableCell.textField.text = [self.settingsDictionary objectForKey:@"password"];
+                editableCell.textField.text = dummy;
                 editableCell.textField.secureTextEntry = YES;
                 editableCell.tag = 50;
+                [dummy release];
             }
             
             editableCell.accessoryView = nil;
@@ -208,16 +224,34 @@
             }
             
             switchContent = (UISwitch *)cell.accessoryView;
-            if (row == 0) {
-                cell.textLabel.text = NSLocalizedString(@"Alternate Damage", @"");
-                cell.detailTextLabel.text = NSLocalizedString(@"Damage popups will notify you on every single hit", @"");
-                switchContent.on = [[self.settingsDictionary objectForKey:@"alternate"] boolValue];
-                switchContent.tag = 30;
-            } else {
-                cell.textLabel.text = NSLocalizedString(@"Classic Ammo Menu", @"");
-                cell.detailTextLabel.text = NSLocalizedString(@"Select which style of ammo menu you prefer",@"");
-                switchContent.on = [[self.settingsDictionary objectForKey:@"menu"] boolValue];
-                switchContent.tag = 60;
+            switch (row) {
+                case 0:
+                    cell.textLabel.text = NSLocalizedString(@"Alternate Damage", @"");
+                    cell.detailTextLabel.text = NSLocalizedString(@"Damage popups will notify you on every single hit", @"");
+                    switchContent.on = [[self.settingsDictionary objectForKey:@"alternate"] boolValue];
+                    switchContent.tag = 30;
+                    break;
+                case 1:
+                    cell.textLabel.text = NSLocalizedString(@"Enanched Graphics Mode", @"");
+                    cell.detailTextLabel.text = NSLocalizedString(@"The game will use more memory so it could crash!", @"");
+                    switchContent.on = [[self.settingsDictionary objectForKey:@"enhanced"] boolValue];
+                    switchContent.tag = 70;
+                    break;
+                case 2:
+                    cell.textLabel.text = NSLocalizedString(@"Multitasking Enabled", @"");
+                    cell.detailTextLabel.text = NSLocalizedString(@"Disable it in case of issues when returing in game", @"");
+                    switchContent.on = [[self.settingsDictionary objectForKey:@"multitasking"] boolValue];
+                    switchContent.tag = 80;
+                    break;
+                case 3:
+                    cell.textLabel.text = NSLocalizedString(@"Classic Ammo Menu", @"");
+                    cell.detailTextLabel.text = NSLocalizedString(@"Select which style of ammo menu you prefer",@"");
+                    switchContent.on = [[self.settingsDictionary objectForKey:@"menu"] boolValue];
+                    switchContent.tag = 60;
+                    break;
+                default:
+                    DLog(@"Nope");
+                    break;
             }
             break;
         default:
--- a/project_files/HedgewarsMobile/Classes/MainMenuViewController.m	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/MainMenuViewController.m	Thu Dec 23 17:47:50 2010 +0100
@@ -27,6 +27,7 @@
 #import "SplitViewRootController.h"
 #import "AboutViewController.h"
 #import "SavedGamesViewController.h"
+#import "ServerSetup.h"
 
 @implementation MainMenuViewController
 @synthesize gameConfigViewController, settingsViewController, aboutViewController, savedGamesViewController;
@@ -124,6 +125,15 @@
         [userDefaults synchronize];
         [self createNecessaryFiles];
     }
+    
+    ServerSetup *setup = [[ServerSetup alloc] init];
+    if ([setup isNetworkReachable]) {
+        DLog(@"network is reachable");
+        [NSThread detachNewThreadSelector:@selector(serverProtocol)
+                                 toTarget:setup
+                               withObject:nil];
+    }
+    [setup release];
 }
 
 
--- a/project_files/HedgewarsMobile/Classes/OverlayViewController.m	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/OverlayViewController.m	Thu Dec 23 17:47:50 2010 +0100
@@ -509,11 +509,25 @@
 
 #pragma mark -
 #pragma mark Custom touch event handling
+-(BOOL) shouldIgnoreTouch:(NSSet *)allTouches {
+    if (isGameRunning() == NO)
+        return YES;
+
+    // ignore activity near the dpad and buttons
+    CGPoint touchPoint = [[[allTouches allObjects] objectAtIndex:0] locationInView:self.view];
+    CGSize screen = [[UIScreen mainScreen] bounds].size;
+
+    if ((touchPoint.x < 160 && touchPoint.y > screen.width - 155 ) || 
+        (touchPoint.x > screen.height - 135 && touchPoint.y > screen.width - 140))
+        return YES;
+    return NO;
+}
+
 -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     NSSet *allTouches = [event allTouches];
     UITouch *first, *second;
 
-    if (isGameRunning() == NO)
+    if ([self shouldIgnoreTouch:allTouches] == YES)
         return;
 
     // hide in-game menu
@@ -552,9 +566,9 @@
     NSSet *allTouches = [event allTouches];
     CGPoint currentPosition = [[[allTouches allObjects] objectAtIndex:0] locationInView:self.view];
 
-    if (isGameRunning() == NO)
+    if ([self shouldIgnoreTouch:allTouches] == YES)
         return;
-    
+
     switch ([allTouches count]) {
         case 1:
             // if we're in the menu we just click in the point
@@ -644,9 +658,9 @@
     int x, y, dx, dy;
     UITouch *touch, *first, *second;
 
-    if (isGameRunning() == NO)
+    if ([self shouldIgnoreTouch:allTouches] == YES)
         return;
-    
+
     switch ([allTouches count]) {
         case 1:
             touch = [[allTouches allObjects] objectAtIndex:0];
--- a/project_files/HedgewarsMobile/Classes/SDL_uikitappdelegate.m	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/SDL_uikitappdelegate.m	Thu Dec 23 17:47:50 2010 +0100
@@ -121,10 +121,10 @@
     // pull out useful configuration info from various files
     GameSetup *setup = [[GameSetup alloc] initWithDictionary:gameDictionary];
     NSNumber *isNetGameNum = [gameDictionary objectForKey:@"netgame"];
-    
-    [setup startThread:@"engineProtocol"];
-    if ([isNetGameNum boolValue] == YES)
-        [setup startThread:@"serverProtocol"];
+
+    [NSThread detachNewThreadSelector:@selector(engineProtocol)
+                             toTarget:setup
+                           withObject:nil];
 
     const char **gameArgs = [setup getSettings:[gameDictionary objectForKey:@"savefile"]];
     NSNumber *menuStyle = [NSNumber numberWithBool:setup.menuStyle];
@@ -232,8 +232,9 @@
     if ([device respondsToSelector:@selector(isMultitaskingSupported)] &&
          device.multitaskingSupported &&
          self.isInGame) {
-        // multiasking in-game works only for ios >= 4.2, returns a black screen on other verions
-        if (NSClassFromString(@"UIPrintInfo"))
+        // let's try to be permissive with multitasking here...
+        NSDictionary *settings = [[NSDictionary alloc] initWithContentsOfFile:SETTINGS_FILE()];
+        if ([[settings objectForKey:@"multitasking"] boolValue])
             HW_suspend();
         else {
             // so the game returns to the configuration view
@@ -246,6 +247,7 @@
                 longjmp(*(jump_env()), 1);
             }
         }
+        [settings release];
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/ServerSetup.h	Thu Dec 23 17:47:50 2010 +0100
@@ -0,0 +1,35 @@
+/*
+ * Hedgewars-iOS, a Hedgewars port for iOS devices
+ * Copyright (c) 2009-2010 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * File created on 16/12/2010.
+ */
+
+
+#import <Foundation/Foundation.h>
+#import "SDL_net.h"
+
+@interface ServerSetup : NSObject {
+    NSDictionary *systemSettings;
+
+    TCPsocket sd;      // External socket descriptor
+}
+
+@property (nonatomic, retain) NSDictionary *systemSettings;
+
+-(BOOL) isNetworkReachable;
+
+@end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/ServerSetup.m	Thu Dec 23 17:47:50 2010 +0100
@@ -0,0 +1,221 @@
+/*
+ * Hedgewars-iOS, a Hedgewars port for iOS devices
+ * Copyright (c) 2009-2010 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * File created on 10/01/2010.
+ */
+
+
+#import "ServerSetup.h"
+#import "PascalImports.h"
+#import "CommodityFunctions.h"
+#import <SystemConfiguration/SCNetworkReachability.h>
+#import <netinet/in.h>
+
+#define BUFFER_SIZE 256
+
+@implementation ServerSetup
+@synthesize systemSettings;
+
+-(id) init {
+    if (self = [super init]) {
+        NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:SETTINGS_FILE()];
+        self.systemSettings = dict;
+        [dict release];
+    }
+    return self;
+}
+
+-(void) dealloc {
+
+    [super dealloc];
+}
+
+// reusing appirater method
+-(BOOL) isNetworkReachable {
+    // Create zero addy
+    struct sockaddr_in zeroAddress;
+    bzero(&zeroAddress, sizeof(zeroAddress));
+    zeroAddress.sin_len = sizeof(zeroAddress);
+    zeroAddress.sin_family = AF_INET;
+    
+    // Recover reachability flags
+    SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
+    SCNetworkReachabilityFlags flags;
+    
+    BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
+    CFRelease(defaultRouteReachability);
+    
+    if (!didRetrieveFlags) {
+        NSLog(@"Error. Could not recover network reachability flags");
+        return NO;
+    }
+    
+    BOOL isReachable = flags & kSCNetworkFlagsReachable;
+    BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
+    BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection;
+    
+    NSURL *testURL = [NSURL URLWithString:@"http://www.apple.com/"];
+    NSURLRequest *testRequest = [NSURLRequest requestWithURL:testURL
+                                                 cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
+                                             timeoutInterval:20.0];
+    NSURLConnection *testConnection = [[NSURLConnection alloc] initWithRequest:testRequest delegate:self];
+    
+    return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO;
+}
+
+-(int) sendToServer:(NSString *)command {
+    NSString *message = [[NSString alloc] initWithFormat:@"%@\n\n",command];
+    int result = SDLNet_TCP_Send(sd, [message UTF8String], [message length]);
+    [message release];
+    return result;
+}
+
+-(int) sendToServer:(NSString *)command withArgument:(NSString *)argument {
+    NSString *message = [[NSString alloc] initWithFormat:@"%@\n%@\n\n",command,argument];
+    int result = SDLNet_TCP_Send(sd, [message UTF8String], [message length]);
+    [message release];
+    return result;
+}
+
+-(void) serverProtocol {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    IPaddress ip;
+    BOOL clientQuit = NO;
+    char *buffer = (char *)malloc(sizeof(char)*BUFFER_SIZE);
+    int dim = BUFFER_SIZE;
+    uint8_t msgSize;
+
+    if (SDLNet_Init() < 0) {
+        DLog(@"SDLNet_Init: %s", SDLNet_GetError());
+        clientQuit = YES;
+    }
+
+    // Resolving the host using NULL make network interface to listen
+    if (SDLNet_ResolveHost(&ip, "netserver.hedgewars.org", DEFAULT_NETGAME_PORT) < 0 && !clientQuit) {
+        DLog(@"SDLNet_ResolveHost: %s\n", SDLNet_GetError());
+        clientQuit = YES;
+    }
+
+    // Open a connection with the IP provided (listen on the host's port)
+    if (!(sd = SDLNet_TCP_Open(&ip)) && !clientQuit) {
+        DLog(@"SDLNet_TCP_Open: %s %\n", SDLNet_GetError(), DEFAULT_NETGAME_PORT);
+        clientQuit = YES;
+    }
+
+    DLog(@"Found server on port %d", DEFAULT_NETGAME_PORT);
+    while (!clientQuit) {
+        int index = 0;
+        BOOL exitBufferLoop = NO;
+        memset(buffer, '\0', dim);
+        
+        while (exitBufferLoop != YES) {
+            msgSize = SDLNet_TCP_Recv(sd, &buffer[index], 2);
+            
+            // exit in case of error
+            if (msgSize <= 0) {
+                DLog(@"SDLNet_TCP_Recv: %s", SDLNet_GetError());
+                clientQuit = YES;
+                break;
+            }
+            
+            // update index position and check for End-Of-Message
+            index += msgSize;
+            if (strncmp(&buffer[index-2], "\n\n", 2) == 0) {
+                exitBufferLoop = YES;
+            }
+            
+            // if message is too big allocate new space
+            if (index >= dim) {
+                dim += BUFFER_SIZE;
+                buffer = (char *)realloc(buffer, dim);
+                if (buffer == NULL) {
+                    clientQuit = YES;
+                    break;
+                }
+            }
+        }
+
+        NSString *bufferedMessage = [[NSString alloc] initWithBytes:buffer length:index-2 encoding:NSASCIIStringEncoding];
+        NSArray *listOfCommands = [bufferedMessage componentsSeparatedByString:@"\n"];
+        [bufferedMessage release];
+        NSString *command = [listOfCommands objectAtIndex:0];
+        DLog(@"size = %d, %@", index-2, listOfCommands);
+        if ([command isEqualToString:@"PING"]) {
+            if ([listOfCommands count] > 1)
+                [self sendToServer:@"PONG" withArgument:[listOfCommands objectAtIndex:1]];
+            else
+                [self sendToServer:@"PONG"];
+            DLog(@"PONG");
+        }
+        else if ([command isEqualToString:@"NICK"]) {
+            //what is this for?
+        }
+        else if ([command isEqualToString:@"PROTO"]) {
+            //what is this for?
+        }
+        else if ([command isEqualToString:@"ROOM"]) {
+            //TODO: stub
+        }
+        else if ([command isEqualToString:@"LOBBY:LEFT"]) {
+            //TODO: stub
+        }
+        else if ([command isEqualToString:@"LOBBY:JOINED"]) {
+            //TODO: stub
+        }
+        else if ([command isEqualToString:@"ASKPASSWORD"]) {
+            NSString *pwd = [self.systemSettings objectForKey:@"password"];
+            [self sendToServer:@"PASSWORD" withArgument:pwd];
+        }
+        else if ([command isEqualToString:@"CONNECTED"]) {
+            short int netProto;
+            char *versionStr;
+            HW_versionInfo(&netProto, &versionStr);
+            NSString *nick = [self.systemSettings objectForKey:@"username"];
+            [self sendToServer:@"NICK" withArgument:nick];
+            [self sendToServer:@"PROTO" withArgument:[NSString stringWithFormat:@"%d",netProto]];
+        }
+        else if ([command isEqualToString:@"SERVER_MESSAGE"]) {
+            DLog(@"%@", [listOfCommands objectAtIndex:1]);
+        }
+        else if ([command isEqualToString:@"WARNING"]) {
+            if ([listOfCommands count] > 1)
+                DLog(@"Server warning - %@", [listOfCommands objectAtIndex:1]);
+            else
+                DLog(@"Server warning - unknown");
+        }
+        else if ([command isEqualToString:@"ERROR"]) {
+            DLog(@"Server error - %@", [listOfCommands objectAtIndex:1]);
+        }
+        else if ([command isEqualToString:@"BYE"]) {
+            //TODO: handle "Reconnected too fast"
+            DLog(@"Server disconnected, reason: %@", [listOfCommands objectAtIndex:1]);
+            clientQuit = YES;
+        }
+        else {
+            DLog(@"Unknown/Unsupported message received: %@", command);
+        }
+    }
+    DLog(@"Server closed connection, ending thread");
+
+    free(buffer);
+    SDLNet_TCP_Close(sd);
+    SDLNet_Quit();
+
+    [pool release];
+}
+
+@end
--- a/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m	Thu Dec 23 17:47:50 2010 +0100
@@ -222,21 +222,22 @@
 }
 
 -(CGFloat) tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
-    return 20;
+    return IS_IPAD() ? 40 : 20;
 }
 
 -(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger) section {
-    UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 20)];
+    NSInteger height = IS_IPAD() ? 40 : 20;
+    UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, height)];
     footer.backgroundColor = [UIColor clearColor];
 
-    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width*80/100, 20)];
-    label.center = CGPointMake(self.tableView.frame.size.width/2, 20/2);
+    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width*80/100, height)];
+    label.center = CGPointMake(self.tableView.frame.size.width/2, height/2);
     label.textAlignment = UITextAlignmentCenter;
     label.font = [UIFont italicSystemFontOfSize:12];
     label.textColor = [UIColor whiteColor];
-    label.numberOfLines = 1;
+    label.numberOfLines = 2;
     if (section == 0)
-        label.text = NSLocalizedString(@"Tap to add hogs or change color, touch and hold to remove team.",@"");
+        label.text = NSLocalizedString(@"Tap to add hogs or change color, touch and hold to remove a team.",@"");
     else
         label.text = NSLocalizedString(@"The robot badge indicates an AI-controlled team.",@"");
 
--- a/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj	Thu Dec 23 17:47:50 2010 +0100
@@ -191,6 +191,7 @@
 		61DF0EDC1284DF2300F3F10B /* HelpPageLobbyViewController-iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 61DF0EDB1284DF2300F3F10B /* HelpPageLobbyViewController-iPhone.xib */; };
 		61DF0F211284F72A00F3F10B /* HelpPageInGameViewController-iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 61DF0F201284F72A00F3F10B /* HelpPageInGameViewController-iPhone.xib */; };
 		61E1F4F811D004240016A5AA /* adler32.pas in Sources */ = {isa = PBXBuildFile; fileRef = 61E1F4F711D004240016A5AA /* adler32.pas */; };
+		61E2E12E12BAAEE30051B659 /* ServerSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = 61E2E12D12BAAEE30051B659 /* ServerSetup.m */; };
 		61E2F7441283752C00E12521 /* fb.png in Resources */ = {isa = PBXBuildFile; fileRef = 61E2F7421283752C00E12521 /* fb.png */; };
 		61E2F7451283752C00E12521 /* tw.png in Resources */ = {isa = PBXBuildFile; fileRef = 61E2F7431283752C00E12521 /* tw.png */; };
 		61E5D68D12AB006F00566F29 /* uLandPainted.pas in Sources */ = {isa = PBXBuildFile; fileRef = 61E5D68C12AB006F00566F29 /* uLandPainted.pas */; };
@@ -971,6 +972,8 @@
 		61DF0EDB1284DF2300F3F10B /* HelpPageLobbyViewController-iPhone.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "HelpPageLobbyViewController-iPhone.xib"; path = "../Resources/HelpPageLobbyViewController-iPhone.xib"; sourceTree = "<group>"; };
 		61DF0F201284F72A00F3F10B /* HelpPageInGameViewController-iPhone.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "HelpPageInGameViewController-iPhone.xib"; path = "Resources/HelpPageInGameViewController-iPhone.xib"; sourceTree = SOURCE_ROOT; };
 		61E1F4F711D004240016A5AA /* adler32.pas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = adler32.pas; path = ../../hedgewars/adler32.pas; sourceTree = SOURCE_ROOT; };
+		61E2E12C12BAAEE30051B659 /* ServerSetup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerSetup.h; sourceTree = "<group>"; };
+		61E2E12D12BAAEE30051B659 /* ServerSetup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServerSetup.m; sourceTree = "<group>"; };
 		61E2F7421283752C00E12521 /* fb.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = fb.png; path = Resources/Icons/fb.png; sourceTree = "<group>"; };
 		61E2F7431283752C00E12521 /* tw.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = tw.png; path = Resources/Icons/tw.png; sourceTree = "<group>"; };
 		61E5D68C12AB006F00566F29 /* uLandPainted.pas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = uLandPainted.pas; path = ../../hedgewars/uLandPainted.pas; sourceTree = SOURCE_ROOT; };
@@ -1046,6 +1049,8 @@
 				6165924C11CA9CB400D6E256 /* MainMenuViewController-iPhone.xib */,
 				616591E611CA9BA200D6E256 /* GameSetup.h */,
 				616591E711CA9BA200D6E256 /* GameSetup.m */,
+				61E2E12C12BAAEE30051B659 /* ServerSetup.h */,
+				61E2E12D12BAAEE30051B659 /* ServerSetup.m */,
 			);
 			path = Classes;
 			sourceTree = "<group>";
@@ -2288,7 +2293,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "#copy new stuff over old stuff\nrm -rf ${PROJECT_DIR}/Data\ncp -R ${PROJECT_DIR}/../../share/hedgewars/Data ${PROJECT_DIR}/Data\n\n#copy some files from QTfrontend/res\nmkdir ${PROJECT_DIR}/Data/Graphics/Btn\ncp ${PROJECT_DIR}/../../QTfrontend/res/btn*.png ${PROJECT_DIR}/Data/Graphics/Btn\ncp ${PROJECT_DIR}/../../QTfrontend/res/ammopic.png ${PROJECT_DIR}/Data/Graphics/Btn/iconAmmo.png\ncp ${PROJECT_DIR}/../../QTfrontend/res/icon*.png ${PROJECT_DIR}/Data/Graphics/Btn\ncp -R ${PROJECT_DIR}/../../QTfrontend/res/botlevels ${PROJECT_DIR}/Data/Graphics/Hedgehog/botlevels\n\n#delete all CMakeLists.txt and image source files\nfind ${PROJECT_DIR}/Data -name CMakeLists.txt -delete\nfind ${PROJECT_DIR}/Data -name *.svg -delete\nfind ${PROJECT_DIR}/Data -name *.svgz -delete\nfind ${PROJECT_DIR}/Data -name *.sifz -delete\nfind ${PROJECT_DIR}/Data -name *.xcf -delete\nfind ${PROJECT_DIR}/Data -name *.orig -delete\n\n#delete desktop frontend translation\nrm -rf ${PROJECT_DIR}/Data/Locale/hedgewars_*\n\n#delete dummy maps and hats\nrm -rf ${PROJECT_DIR}/Data/Maps/{test*,Ruler}\nrm -rf ${PROJECT_DIR}/Data/Hats/{TeamCap,TeamHeadband,TeamHair}\n\n#copy mono audio\ncp -R ${PROJECT_DIR}/audio/* ${PROJECT_DIR}/Data/\n\n#the following ones must be removed\nrm -rf ${PROJECT_DIR}/Data/Maps/{Cheese,FlightJoust}\n\n#move Lua maps in Missions\nmkdir ${PROJECT_DIR}/Data/Missions/Maps/\nmv ${PROJECT_DIR}/Data/Maps/{Basketball,Knockball,TrophyRace} ${PROJECT_DIR}/Data/Missions/Maps/\n\n#reduce the number of flakes for City\nawk '{if ($1 == 1500) $1=40; print $0}' < ${PROJECT_DIR}/Data/Themes/City/theme.cfg > /tmp/tempfile\nmv /tmp/tempfile ${PROJECT_DIR}/Data/Themes/City/theme.cfg\n\n#remove Isalnd from the list of Themes\nawk '{if ($1 != \"Island\") print $0}' < ${PROJECT_DIR}/Data/Themes/themes.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Themes/themes.cfg\n\n#remove Isalnd from the Maps and themes\nrm -rf ${PROJECT_DIR}/Data/Themes/Island\nawk '{if ($1 == \"Island\") print \"Nature\"}' < ${PROJECT_DIR}/Data/Maps/Cave/map.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Maps/Cave/map.cfg\nawk '{if ($1 == \"Island\") print \"Nature\"}' < ${PROJECT_DIR}/Data/Maps/Lonely_Island/map.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Maps/Lonely_Island/map.cfg\nawk '{if ($1 == \"Island\") print \"Nature\"}' < ${PROJECT_DIR}/Data/Maps/PirateFlag/map.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Maps/PirateFlag/map.cfg\n\n#delete the Classic voice\nrm -rf ${PROJECT_DIR}/Data/Sounds/voices/Classic\n\n#delete useless fonts\nrm -rf ${PROJECT_DIR}/Data/Fonts/{wqy-zenhei.ttc,DroidSansFallback.ttf}\n\n#delete all names, reserved hats\nrm -rf ${PROJECT_DIR}/Data/Names/\nrm -rf ${PROJECT_DIR}/Data/Graphics/Hats/Reserved/\n";
+			shellScript = "#copy new stuff over old stuff\nrm -rf ${PROJECT_DIR}/Data\ncp -R ${PROJECT_DIR}/../../share/hedgewars/Data ${PROJECT_DIR}/Data\n\n#copy some files from QTfrontend/res\nmkdir ${PROJECT_DIR}/Data/Graphics/Btn\ncp ${PROJECT_DIR}/../../QTfrontend/res/btn*.png ${PROJECT_DIR}/Data/Graphics/Btn\ncp ${PROJECT_DIR}/../../QTfrontend/res/ammopic.png ${PROJECT_DIR}/Data/Graphics/Btn/iconAmmo.png\ncp ${PROJECT_DIR}/../../QTfrontend/res/icon*.png ${PROJECT_DIR}/Data/Graphics/Btn\ncp -R ${PROJECT_DIR}/../../QTfrontend/res/botlevels ${PROJECT_DIR}/Data/Graphics/Hedgehog/botlevels\n\n#delete all CMakeLists.txt and image source files\nfind ${PROJECT_DIR}/Data -name CMakeLists.txt -delete\nfind ${PROJECT_DIR}/Data -name *.svg -delete\nfind ${PROJECT_DIR}/Data -name *.svgz -delete\nfind ${PROJECT_DIR}/Data -name *.sifz -delete\nfind ${PROJECT_DIR}/Data -name *.xcf -delete\nfind ${PROJECT_DIR}/Data -name *.orig -delete\n\n#delete desktop frontend translation\nrm -rf ${PROJECT_DIR}/Data/Locale/hedgewars_*\n\n#delete dummy maps and hats\nrm -rf ${PROJECT_DIR}/Data/Maps/{test*,Ruler}\nrm -rf ${PROJECT_DIR}/Data/Hats/{TeamCap,TeamHeadband,TeamHair}\nrm -rf ${PROJECT_DIR}/Data/misc/\n\n#copy mono audio\ncp -R ${PROJECT_DIR}/audio/* ${PROJECT_DIR}/Data/\n\n#the following ones must be removed\nrm -rf ${PROJECT_DIR}/Data/Maps/{Cheese,FlightJoust}\n\n#move Lua maps in Missions\nmkdir ${PROJECT_DIR}/Data/Missions/Maps/\nmv ${PROJECT_DIR}/Data/Maps/{Basketball,Knockball,TrophyRace} ${PROJECT_DIR}/Data/Missions/Maps/\n\n#reduce the number of flakes for City\nawk '{if ($1 == 1500) $1=40; print $0}' < ${PROJECT_DIR}/Data/Themes/City/theme.cfg > /tmp/tempfile\nmv /tmp/tempfile ${PROJECT_DIR}/Data/Themes/City/theme.cfg\n\n#remove Isalnd from the list of Themes\nawk '{if ($1 != \"Island\") print $0}' < ${PROJECT_DIR}/Data/Themes/themes.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Themes/themes.cfg\n\n#remove Isalnd from the Maps and themes\nrm -rf ${PROJECT_DIR}/Data/Themes/Island\nawk '{if ($1 == \"Island\") print \"Nature\"}' < ${PROJECT_DIR}/Data/Maps/Cave/map.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Maps/Cave/map.cfg\nawk '{if ($1 == \"Island\") print \"Nature\"}' < ${PROJECT_DIR}/Data/Maps/Lonely_Island/map.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Maps/Lonely_Island/map.cfg\nawk '{if ($1 == \"Island\") print \"Nature\"}' < ${PROJECT_DIR}/Data/Maps/PirateFlag/map.cfg > /tmp/tempfile && mv /tmp/tempfile ${PROJECT_DIR}/Data/Maps/PirateFlag/map.cfg\n\n#delete the Classic voice\nrm -rf ${PROJECT_DIR}/Data/Sounds/voices/Classic\n\n#delete useless fonts\nrm -rf ${PROJECT_DIR}/Data/Fonts/{wqy-zenhei.ttc,DroidSansFallback.ttf}\n\n#delete all names, reserved hats\nrm -rf ${PROJECT_DIR}/Data/Names/\nrm -rf ${PROJECT_DIR}/Data/Graphics/Hats/Reserved/\n";
 			showEnvVarsInLog = 0;
 		};
 		9283011B0F10CB2D00CC5A3C /* Build libfpc.a */ = {
@@ -2418,6 +2423,7 @@
 				61E5D68D12AB006F00566F29 /* uLandPainted.pas in Sources */,
 				61F544C712AF1748007FD913 /* HoldTableViewCell.m in Sources */,
 				61AC067412B2E32D000B52A2 /* Appirater.m in Sources */,
+				61E2E12E12BAAEE30051B659 /* ServerSetup.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
--- a/project_files/HedgewarsMobile/Info.plist	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Info.plist	Thu Dec 23 17:47:50 2010 +0100
@@ -28,7 +28,7 @@
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>1.2</string>
+	<string>1.2.1</string>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
 	<key>UILaunchImageFile~ipad</key>
--- a/project_files/HedgewarsMobile/Resources/HelpPageInGameViewController-iPhone.xib	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/HelpPageInGameViewController-iPhone.xib	Thu Dec 23 17:47:50 2010 +0100
@@ -360,7 +360,7 @@
 						<string key="NSFrame">{{-1, 0}, {481, 44}}</string>
 						<reference key="NSSuperview" ref="766721923"/>
 						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<object class="NSMutableArray" key="IBUIItems">
+						<object class="NSArray" key="IBUIItems">
 							<bool key="EncodedWithXMLCoder">YES</bool>
 							<object class="IBUINavigationItem" id="477618874">
 								<reference key="IBUINavigationBar" ref="126317414"/>
@@ -368,6 +368,7 @@
 								<object class="IBUIBarButtonItem" key="IBUILeftBarButtonItem" id="843591489">
 									<string key="IBUITitle">Back</string>
 									<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+									<int key="IBUIStyle">1</int>
 									<reference key="IBUINavigationItem" ref="477618874"/>
 								</object>
 								<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
--- a/project_files/HedgewarsMobile/Resources/OverlayViewController.xib	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/OverlayViewController.xib	Thu Dec 23 17:47:50 2010 +0100
@@ -42,40 +42,6 @@
 				<int key="NSvFlags">274</int>
 				<object class="NSMutableArray" key="NSSubviews">
 					<bool key="EncodedWithXMLCoder">YES</bool>
-					<object class="IBUIView" id="442546943">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">268</int>
-						<string key="NSFrame">{{0, 170}, {180, 150}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<object class="NSColor" key="IBUIBackgroundColor">
-							<int key="NSColorSpace">3</int>
-							<bytes key="NSWhite">MQA</bytes>
-							<object class="NSColorSpace" key="NSCustomColorSpace" id="692152543">
-								<int key="NSID">2</int>
-							</object>
-						</object>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
-						<float key="IBUIAlpha">0.0</float>
-						<bool key="IBUIUserInteractionEnabled">NO</bool>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-					</object>
-					<object class="IBUIView" id="358748789">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">265</int>
-						<string key="NSFrame">{{337, 187}, {143, 133}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<object class="NSColor" key="IBUIBackgroundColor">
-							<int key="NSColorSpace">3</int>
-							<bytes key="NSWhite">MQA</bytes>
-							<reference key="NSCustomColorSpace" ref="692152543"/>
-						</object>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
-						<float key="IBUIAlpha">0.0</float>
-						<bool key="IBUIUserInteractionEnabled">NO</bool>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-					</object>
 					<object class="IBUIButton" id="584263820">
 						<reference key="NSNextResponder" ref="191373211"/>
 						<int key="NSvFlags">268</int>
@@ -628,8 +594,6 @@
 							<reference ref="123494776"/>
 							<reference ref="132251648"/>
 							<reference ref="752933969"/>
-							<reference ref="358748789"/>
-							<reference ref="442546943"/>
 							<reference ref="50885250"/>
 							<reference ref="309477778"/>
 						</object>
@@ -698,18 +662,6 @@
 						<reference key="object" ref="50885250"/>
 						<reference key="parent" ref="191373211"/>
 					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">71</int>
-						<reference key="object" ref="442546943"/>
-						<reference key="parent" ref="191373211"/>
-						<string key="objectName">safezoneleft</string>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">72</int>
-						<reference key="object" ref="358748789"/>
-						<reference key="parent" ref="191373211"/>
-						<string key="objectName">safezoneright</string>
-					</object>
 				</object>
 			</object>
 			<object class="NSMutableDictionary" key="flattenedProperties">
@@ -721,6 +673,7 @@
 					<string>1.IBEditorWindowLastContentRect</string>
 					<string>1.IBPluginDependency</string>
 					<string>12.IBPluginDependency</string>
+					<string>12.IBViewBoundsToFrameTransform</string>
 					<string>17.IBPluginDependency</string>
 					<string>22.IBPluginDependency</string>
 					<string>4.IBPluginDependency</string>
@@ -729,18 +682,17 @@
 					<string>53.IBPluginDependency</string>
 					<string>58.IBPluginDependency</string>
 					<string>67.IBPluginDependency</string>
-					<string>71.IBPluginDependency</string>
-					<string>72.IBPluginDependency</string>
 				</object>
 				<object class="NSMutableArray" key="dict.values">
 					<bool key="EncodedWithXMLCoder">YES</bool>
 					<string>OverlayViewController</string>
 					<string>UIResponder</string>
-					<string>{{567, 304}, {480, 320}}</string>
+					<string>{{690, 375}, {480, 320}}</string>
 					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
 					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABCkAAAw5SAAA</bytes>
+					</object>
 					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
 					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
 					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
--- a/project_files/HedgewarsMobile/Resources/Settings/settings.plist	Tue Dec 14 22:32:47 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/settings.plist	Thu Dec 23 17:47:50 2010 +0100
@@ -14,5 +14,11 @@
 	<string></string>
 	<key>menu</key>
 	<false/>
+	<key>password_length</key>
+	<integer>0</integer>
+	<key>enhanced</key>
+	<false/>
+	<key>multitasking</key>
+	<true/>
 </dict>
 </plist>
Binary file share/hedgewars/Data/Graphics/AmmoMenu/Ammos.png has changed
Binary file share/hedgewars/Data/Graphics/AmmoMenu/Ammos_bw.png has changed
Binary file share/hedgewars/Data/Graphics/Hats/WhySoSerious.png has changed
Binary file share/hedgewars/Data/Graphics/Hedgehog/amSnowball.png has changed
Binary file share/hedgewars/Data/Graphics/Snowball.png has changed
--- a/share/hedgewars/Data/Locale/en.txt	Tue Dec 14 22:32:47 2010 +0100
+++ b/share/hedgewars/Data/Locale/en.txt	Thu Dec 23 17:47:50 2010 +0100
@@ -51,6 +51,7 @@
 00:48=Hammer
 00:49=Resurrector
 00:50=Drill Strike
+00:51=Snowball
 
 01:00=Let's fight!
 01:01=Round draw
--- a/share/hedgewars/Data/Locale/sk.txt	Tue Dec 14 22:32:47 2010 +0100
+++ b/share/hedgewars/Data/Locale/sk.txt	Thu Dec 23 17:47:50 2010 +0100
@@ -50,6 +50,8 @@
 00:47=Lepkavá mína
 00:48=Kladivo
 00:49=Oživovač
+00:50=Útok raketovými vŕtačkami
+00:51=Blatová guľa
 
 01:00=Do boja!
 01:01=Remíza
--- a/share/hedgewars/Data/Scripts/Multiplayer/RandomWeapon.cfg	Tue Dec 14 22:32:47 2010 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-Default
-locked
--- a/share/hedgewars/Data/Scripts/Multiplayer/RandomWeapon.lua	Tue Dec 14 22:32:47 2010 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-local weapons = { amGrenade, amClusterBomb, amBazooka, amBee, amShotgun,
-            amMine, amDEagle, amDynamite, amFirePunch, amWhip, amPickHammer,
-            amBaseballBat, amAirAttack, amMineStrike, amTeleport, amMortar, amCake,
-            amSeduction, amWatermelon, amHellishBomb, amNapalm, amDrill, amBallgun,
-            amRCPlane, amSniperRifle, amMolotov, amBirdy, amBlowTorch,
-            amGasBomb, amFlamethrower, amSMine, amHammer, amDrillStrike }
-
-local lastRound = -1
-local weapon = 0
-
-function onGameInit()
-    GameFlags = band(bor(GameFlags, gfResetWeps), bnot(gfInfAttack + gfPerHogAmmo))
-end
-
-function onAmmoStoreInit()
-    SetAmmo(amSkip, 9, 0, 0, 0)
-
-    SetAmmo(amParachute, 0, 1, 0, 1)
-    SetAmmo(amGirder, 0, 1, 0, 2)
-    SetAmmo(amSwitch, 0, 1, 0, 1)
-    SetAmmo(amLowGravity, 0, 1, 0, 1)
-    SetAmmo(amExtraDamage, 0, 1, 0, 1)
-    SetAmmo(amInvulnerable, 0, 1, 0, 1)
-    SetAmmo(amExtraTime, 0, 1, 0, 1)
-    SetAmmo(amLaserSight, 0, 1, 0, 1)
-    SetAmmo(amVampiric, 0, 1, 0, 1)
-    SetAmmo(amJetpack, 0, 1, 0, 1)
-    SetAmmo(amPortalGun, 0, 1, 0, 1)
-    SetAmmo(amResurrector, 0, 1, 0, 1)
-
-    for i, w in pairs(weapons) do
-        SetAmmo(w, 0, 0, 0, 1)
-    end
-end
-
-function onNewTurn()
-    if lastRound ~= TotalRounds then
-        weapon = GetRandom(table.maxn(weapons)) + 1
-        lastRound = TotalRounds
-    end
-    AddAmmo(CurrentHedgehog, weapons[weapon])
-end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Scripts/Multiplayer/Random_Weapon.cfg	Thu Dec 23 17:47:50 2010 +0100
@@ -0,0 +1,2 @@
+Default
+locked
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Scripts/Multiplayer/Random_Weapon.lua	Thu Dec 23 17:47:50 2010 +0100
@@ -0,0 +1,42 @@
+local weapons = { amGrenade, amClusterBomb, amBazooka, amBee, amShotgun,
+            amMine, amDEagle, amDynamite, amFirePunch, amWhip, amPickHammer,
+            amBaseballBat, amAirAttack, amMineStrike, amTeleport, amMortar, amCake,
+            amSeduction, amWatermelon, amHellishBomb, amNapalm, amDrill, amBallgun,
+            amRCPlane, amSniperRifle, amMolotov, amBirdy, amBlowTorch,
+            amGasBomb, amFlamethrower, amSMine, amHammer, amDrillStrike }
+
+local lastRound = -1
+local weapon = 0
+
+function onGameInit()
+    GameFlags = band(bor(GameFlags, gfResetWeps), bnot(gfInfAttack + gfPerHogAmmo))
+end
+
+function onAmmoStoreInit()
+    SetAmmo(amSkip, 9, 0, 0, 0)
+
+    SetAmmo(amParachute, 0, 1, 0, 1)
+    SetAmmo(amGirder, 0, 1, 0, 2)
+    SetAmmo(amSwitch, 0, 1, 0, 1)
+    SetAmmo(amLowGravity, 0, 1, 0, 1)
+    SetAmmo(amExtraDamage, 0, 1, 0, 1)
+    SetAmmo(amInvulnerable, 0, 1, 0, 1)
+    SetAmmo(amExtraTime, 0, 1, 0, 1)
+    SetAmmo(amLaserSight, 0, 1, 0, 1)
+    SetAmmo(amVampiric, 0, 1, 0, 1)
+    SetAmmo(amJetpack, 0, 1, 0, 1)
+    SetAmmo(amPortalGun, 0, 1, 0, 1)
+    SetAmmo(amResurrector, 0, 1, 0, 1)
+
+    for i, w in pairs(weapons) do
+        SetAmmo(w, 0, 0, 0, 1)
+    end
+end
+
+function onNewTurn()
+    if lastRound ~= TotalRounds then
+        weapon = GetRandom(table.maxn(weapons)) + 1
+        lastRound = TotalRounds
+    end
+    AddAmmo(CurrentHedgehog, weapons[weapon])
+end
Binary file share/hedgewars/Data/Themes/Compost/Snowball.png has changed
Binary file share/hedgewars/Data/Themes/Compost/amSnowball.png has changed
Binary file share/hedgewars/Data/Themes/Underwater/Snowball.png has changed
Binary file share/hedgewars/Data/Themes/Underwater/amSnowball.png has changed