1 /*
2  *  Copyright (C) 2011-2016  OpenDungeons Team
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "ODSocketServer.h"
19 #include "network/ODPacket.h"
20 #include "game/Player.h"
21 
22 #include "utils/Helper.h"
23 #include "utils/LogManager.h"
24 
25 #include <SFML/System.hpp>
26 
ODSocketServer()27 ODSocketServer::ODSocketServer():
28     mThread(nullptr),
29     mIsConnected(false)
30 {
31 }
32 
~ODSocketServer()33 ODSocketServer::~ODSocketServer()
34 {
35     if(mIsConnected)
36         stopServer();
37 }
38 
createServer(int listeningPort)39 bool ODSocketServer::createServer(int listeningPort)
40 {
41     mIsConnected = false;
42 
43     // As we use selector, there is no need to set the socket as not-blocking
44     sf::Socket::Status status = mSockListener.listen(listeningPort);
45     if (status != sf::Socket::Done)
46     {
47         OD_LOG_ERR("Could not listen to server port status="
48             + Helper::toString(status));
49         return false;
50     }
51 
52     mSockSelector.add(mSockListener);
53     mIsConnected = true;
54     OD_LOG_INF("Server connected and listening");
55     mThread = new sf::Thread(&ODSocketServer::serverThread, this);
56     mThread->launch();
57 
58     return true;
59 }
60 
isConnected()61 bool ODSocketServer::isConnected()
62 {
63     return mIsConnected;
64 }
65 
doTask(int timeoutMs)66 void ODSocketServer::doTask(int timeoutMs)
67 {
68     mClockMainTask.restart();
69     while((timeoutMs == 0) ||
70           (timeoutMs > mClockMainTask.getElapsedTime().asMilliseconds()))
71     {
72         bool isSockReady;
73         if(timeoutMs != 0)
74         {
75             // We adapt the timeout so that the function returns after timeoutMs
76             // even if events occurred
77             int timeoutMsAdjusted = std::max(1, timeoutMs - mClockMainTask.getElapsedTime().asMilliseconds());
78             isSockReady = mSockSelector.wait(sf::milliseconds(timeoutMsAdjusted));
79         }
80         else
81         {
82             isSockReady = mSockSelector.wait(sf::Time::Zero);
83         }
84 
85         // Check if a client tries to connect or to communicate
86         if(!isSockReady)
87             continue;
88 
89         if(mSockSelector.isReady(mSockListener))
90         {
91             // New connection
92             ODSocketClient* newClient = notifyNewConnection(mSockListener);
93             if (newClient != nullptr)
94             {
95                 // New connection
96                 OD_LOG_INF("New client connected.");
97                 // The server wants to keep the client
98                 newClient->setSource(ODSocketClient::ODSource::network);
99                 mSockSelector.add(newClient->getSockClient());
100                 mSockClients.push_back(newClient);
101             }
102         }
103         else
104         {
105 
106             for(std::vector<ODSocketClient*>::iterator it = mSockClients.begin(); it != mSockClients.end();)
107             {
108                 ODSocketClient* client = *it;
109                 if((mSockSelector.isReady(client->getSockClient())) &&
110                     (!notifyClientMessage(client)))
111                 {
112                     // The server wants to remove the client
113                     it = mSockClients.erase(it);
114                     mSockSelector.remove(client->getSockClient());
115                     client->disconnect();
116                     delete client;
117                 }
118                 else
119                 {
120                     ++it;
121                 }
122             }
123         }
124     }
125 }
126 
stopServer()127 void ODSocketServer::stopServer()
128 {
129     mIsConnected = false;
130     if(mThread != nullptr)
131         delete mThread; // Delete waits for the thread to finish
132     mThread = nullptr;
133     mSockSelector.clear();
134     mSockListener.close();
135     for (std::vector<ODSocketClient*>::iterator it = mSockClients.begin(); it != mSockClients.end(); ++it)
136     {
137         ODSocketClient* client = *it;
138         client->disconnect();
139         delete client;
140     }
141 
142     mSockClients.clear();
143 }
144