224 lines
9.1 KiB
C++
224 lines
9.1 KiB
C++
#include "ServerModule.h"
|
|
|
|
ServerModule::ServerModule(Archimedes::App* a, void* h) : Archimedes::Module(a, h) {
|
|
name = "ServerModule";
|
|
}
|
|
|
|
ServerModule::~ServerModule() {
|
|
if(app) {
|
|
app->unregisterEvent(Archimedes::DataRecievedEvent());
|
|
app->unregisterEvent(Archimedes::DataSentEvent());
|
|
app->unregisterEvent(Archimedes::ConnectionStatusChangedEvent());
|
|
|
|
GameNetworkingSockets_Kill();
|
|
}
|
|
}
|
|
|
|
void ServerModule::onLoad() {
|
|
|
|
app->registerEvent(Archimedes::DataRecievedEvent());
|
|
app->registerEvent(Archimedes::DataSentEvent());
|
|
app->registerEvent(Archimedes::ConnectionStatusChangedEvent());
|
|
|
|
SteamDatagramErrMsg errMsg;
|
|
|
|
if ( !GameNetworkingSockets_Init( nullptr, errMsg ) ) {
|
|
std::cerr << "GameNetworkingSockets_Init() Failed: " << errMsg << std::endl;
|
|
}
|
|
|
|
//SteamNetworkingUtils()->SetDebugOutputFunction( k_ESteamNetworkingSocketsDebugOutputType_Msg, DebugOutput );
|
|
|
|
interface = SteamNetworkingSockets();
|
|
|
|
}
|
|
|
|
void ServerModule::startServer(int p) {
|
|
if(!running) {
|
|
port = p;
|
|
|
|
SteamNetworkingIPAddr serverLocalAddr;
|
|
serverLocalAddr.Clear();
|
|
serverLocalAddr.m_port = port;
|
|
SteamNetworkingConfigValue_t opt;
|
|
opt.SetPtr(k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, (void*)SteamNetConnectionStatusChangedCallback);
|
|
|
|
listenSock = interface->CreateListenSocketIP( serverLocalAddr, 1, &opt );
|
|
if ( listenSock == k_HSteamListenSocket_Invalid )
|
|
std::cerr << "Failed to listen on port " << port << std::endl;
|
|
pollGroup = interface->CreatePollGroup();
|
|
if ( pollGroup == k_HSteamNetPollGroup_Invalid )
|
|
std::cerr << "Failed to listen on port " << port << std::endl;
|
|
std::cerr << "Server listening on port " << port << std::endl;
|
|
|
|
running = true;
|
|
}
|
|
}
|
|
|
|
void ServerModule::stopServer() {
|
|
disconnectAllClients();
|
|
running = false;
|
|
}
|
|
|
|
void ServerModule::run() {
|
|
if(running) {
|
|
pollIncomingData();
|
|
PollConnectionStateChanges();
|
|
} else if(port >= 0) {
|
|
|
|
interface->CloseListenSocket(listenSock);
|
|
listenSock = k_HSteamListenSocket_Invalid;
|
|
|
|
interface->DestroyPollGroup(pollGroup);
|
|
pollGroup = k_HSteamNetPollGroup_Invalid;
|
|
|
|
port = -1;
|
|
}
|
|
}
|
|
|
|
|
|
bool ServerModule::onEvent(const Archimedes::Event& event) {
|
|
|
|
unsigned int type = app->getEventType(event);
|
|
|
|
if(eventsToHandle & SMEventEnum::ConnectionStatusChanged && type == app->getEventType(Archimedes::ConnectionStatusChangedEvent())) {
|
|
|
|
Archimedes::ConnectionStatusChangedEvent& e = (Archimedes::ConnectionStatusChangedEvent&) event;
|
|
|
|
switch(e.info->m_info.m_eState) {
|
|
|
|
case k_ESteamNetworkingConnectionState_None:
|
|
// NOTE: We will get callbacks here when we destroy connections. You can ignore these.
|
|
break;
|
|
|
|
case k_ESteamNetworkingConnectionState_ClosedByPeer:
|
|
case k_ESteamNetworkingConnectionState_ProblemDetectedLocally:
|
|
{
|
|
// Ignore if they were not previously connected. (If they disconnected
|
|
// before we accepted the connection.)
|
|
if ( e.info->m_eOldState == k_ESteamNetworkingConnectionState_Connected )
|
|
{
|
|
|
|
// Locate the client. Note that it should have been found, because this
|
|
// is the only codepath where we remove clients (except on shutdown),
|
|
// and connection change callbacks are dispatched in queue order.
|
|
auto itClient = clients.find( e.info->m_hConn );
|
|
assert( itClient != clients.end() );
|
|
|
|
// Select appropriate log messages
|
|
//const char *pszDebugLogAction;
|
|
/*if ( e.info->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally )
|
|
{
|
|
pszDebugLogAction = "problem detected locally";
|
|
sprintf( temp, "Alas, %s hath fallen into shadow. (%s)", itClient->second.name.c_str(), e.info->m_info.m_szEndDebug );
|
|
}
|
|
else
|
|
{
|
|
// Note that here we could check the reason code to see if
|
|
// it was a "usual" connection or an "unusual" one.
|
|
pszDebugLogAction = "closed by peer"; (void)pszDebugLogAction;
|
|
sprintf( temp, "%s hath departed", itClient->second.name.c_str() );
|
|
}*/
|
|
|
|
// Spew something to our own log. Note that because we put their nick
|
|
// as the connection description, it will show up, along with their
|
|
// transport-specific data (e.g. their IP address)
|
|
/*Printf( "Connection %s %s, reason %d: %s\n",
|
|
e.info->m_info.m_szConnectionDescription,
|
|
pszDebugLogAction,
|
|
e.info->m_info.m_eEndReason,
|
|
e.info->m_info.m_szEndDebug
|
|
);*/
|
|
|
|
clients.erase( itClient );
|
|
|
|
// Send a message so everybody else knows what happened
|
|
//SendStringToAllClients( temp );
|
|
}
|
|
else
|
|
{
|
|
assert( e.info->m_eOldState == k_ESteamNetworkingConnectionState_Connecting );
|
|
}
|
|
|
|
// Clean up the connection. This is important!
|
|
// The connection is "closed" in the network sense, but
|
|
// it has not been destroyed. We must close it on our end, too
|
|
// to finish up. The reason information do not matter in this case,
|
|
// and we cannot linger because it's already closed on the other end,
|
|
// so we just pass 0's.
|
|
interface->CloseConnection( e.info->m_hConn, 0, nullptr, false );
|
|
std::cerr << "Connection Closed.\n";
|
|
break;
|
|
}
|
|
|
|
case k_ESteamNetworkingConnectionState_Connecting:
|
|
{
|
|
// This must be a new connection
|
|
assert( clients.find( e.info->m_hConn ) == clients.end() );
|
|
|
|
//Printf( "Connection request from %s", e.info->m_info.m_szConnectionDescription );
|
|
|
|
// A client is attempting to connect
|
|
// Try to accept the connection.
|
|
if ( interface->AcceptConnection( e.info->m_hConn ) != k_EResultOK )
|
|
{
|
|
// This could fail. If the remote host tried to connect, but then
|
|
// disconnected, the connection may already be half closed. Just
|
|
// destroy whatever we have on our side.
|
|
interface->CloseConnection( e.info->m_hConn, 0, nullptr, false );
|
|
//Printf( "Can't accept connection. (It was already closed?)" );
|
|
break;
|
|
}
|
|
std::cerr << "Connection Accepted!\n";
|
|
|
|
// Assign the poll group
|
|
if ( !interface->SetConnectionPollGroup( e.info->m_hConn, pollGroup ) )
|
|
{
|
|
interface->CloseConnection( e.info->m_hConn, 0, nullptr, false );
|
|
//Printf( "Failed to set poll group?" );
|
|
break;
|
|
}
|
|
|
|
// Add them to the client list, using std::map wacky syntax
|
|
clients[ e.info->m_hConn ] = ++numClients;
|
|
//SetClientNick( e.info->m_hConn, nick );
|
|
break;
|
|
}
|
|
|
|
case k_ESteamNetworkingConnectionState_Connected:
|
|
// We will get a callback immediately after accepting the connection.
|
|
// Since we are the server, we can ignore this, it's not news to us.
|
|
break;
|
|
|
|
default:
|
|
// Silences -Wswitch
|
|
break;
|
|
}
|
|
return true;
|
|
|
|
} else if(eventsToHandle & SMEventEnum::DataRecieved && type == app->getEventType(Archimedes::DataRecievedEvent())) {
|
|
return true;
|
|
} else if(eventsToHandle & SMEventEnum::DataSent && type == app->getEventType(Archimedes::DataSentEvent())) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ServerModule::pollIncomingData() {
|
|
|
|
while ( running )
|
|
{
|
|
ISteamNetworkingMessage *pIncomingMsg = nullptr;
|
|
int numMsgs = interface->ReceiveMessagesOnPollGroup( pollGroup, &pIncomingMsg, 1 );
|
|
if ( numMsgs == 0 )
|
|
break;
|
|
if ( numMsgs < 0 )
|
|
std::cerr << "Error checking for messages" << std::endl;
|
|
assert( numMsgs == 1 && pIncomingMsg );
|
|
auto itClient = clients.find( pIncomingMsg->m_conn );
|
|
assert( itClient != clients.end() );
|
|
|
|
app->emitEvent(new Archimedes::DataRecievedEvent(pIncomingMsg));
|
|
}
|
|
}
|