diff --git a/engine/source/core/torqueConfig.h b/engine/source/core/torqueConfig.h index 456c5cf6..5c79dbd4 100644 --- a/engine/source/core/torqueConfig.h +++ b/engine/source/core/torqueConfig.h @@ -20,7 +20,7 @@ /// Version number is major * 1000 + minor * 100 + revision * 10. /// Different engines (TGE, T2D, etc.) will have different version numbers. -#define TORQUE_VERSION 900 // version 0.9 +#define TORQUE_VERSION 901 // version 0.9 /// What engine are we running? The presence and value of this define are /// used to determine what engine (TGE, T2D, etc.) and version thereof we're @@ -46,7 +46,7 @@ #define TORQUE_HIFI_NET /// Define me to enable Torque Hole Punching support -//#define TORQUE_NET_HOLEPUNCHING +#define TORQUE_NET_HOLEPUNCHING /// Define me to enable shader caching support (Unfinished and really only needed for some versions of Wine or Proton) //#define TORQUE_SHADER_CACHING diff --git a/engine/source/game/gameConnection.cpp b/engine/source/game/gameConnection.cpp index 14524b1c..f39d221f 100644 --- a/engine/source/game/gameConnection.cpp +++ b/engine/source/game/gameConnection.cpp @@ -149,8 +149,10 @@ void GameConnection::onConnectionEstablished(bool isInitiator) { setGhostFrom(false); setGhostTo(true); - setSendingEvents(true); - setTranslatesStrings(true); + if (!isEstablished()) { + setSendingEvents(true); + setTranslatesStrings(true); + } setIsConnectionToServer(); mServerConnection = this; Con::printf("Connection established %d", getId()); @@ -160,8 +162,10 @@ void GameConnection::onConnectionEstablished(bool isInitiator) { setGhostFrom(true); setGhostTo(false); - setSendingEvents(true); - setTranslatesStrings(true); + if (!isEstablished()) { + setSendingEvents(true); + setTranslatesStrings(true); + } Sim::getClientGroup()->addObject(this); mTotalServerTicks = ServerTicksUninitialized; diff --git a/engine/source/game/net/serverQuery.cpp b/engine/source/game/net/serverQuery.cpp index e22f8d71..69def015 100644 --- a/engine/source/game/net/serverQuery.cpp +++ b/engine/source/game/net/serverQuery.cpp @@ -94,6 +94,7 @@ // This is basically the server query protocol version now: static const char* versionString = "VER1"; +Vector localNetAddresses; Vector gServerList(__FILE__, __LINE__); static Vector gMasterServerList(__FILE__, __LINE__); static Vector gFinishedList(__FILE__, __LINE__); // timed out servers and finished servers go here @@ -135,6 +136,7 @@ struct Ping U32 time; U32 tryCount; bool broadcast; + bool isLocal; }; static Ping gMasterServerPing; @@ -341,7 +343,7 @@ void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missi U8 filterFlags, bool clearServerInfo, bool useFilters) { sgServerQueryActive = true; - clearServerList(clearServerInfo); + // clearServerList(clearServerInfo); pushServerFavorites(); sActiveFilter.type = useFilters ? ServerFilter::OfflineFiltered : ServerFilter::Offline; @@ -378,7 +380,7 @@ void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missi #endif Con::executef(4, "onServerQueryStatus", "start", "Querying LAN servers", "0"); - processPingsAndQueries(gPingSession); + // processPingsAndQueries(gPingSession); } //----------------------------------------------------------------------------- @@ -406,6 +408,8 @@ ConsoleFunction(queryLanServers, void, 13, 14, "queryLanServers(...);") if (argc >= 14) useFilters = dAtoi(argv[13]) != 0; + clearServerList(); + queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots, regionMask, maxPing, minCPU, filterFlags, clearServerInfo, useFilters); @@ -429,14 +433,14 @@ void queryMasterGameTypes() //----------------------------------------------------------------------------- -void queryMasterServer(U8 flags, const char* gameType, const char* missionType, +void queryMasterServer(U16 lanPort, U8 flags, const char* gameType, const char* missionType, U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, U8 filterFlags, U8 buddyCount, U32* buddyList) { // Reset the list packet flag: gGotFirstListPacket = false; sgServerQueryActive = true; - clearServerList(); + // clearServerList(); Con::executef(4, "onServerQueryStatus", "start", "Querying master server", "0"); @@ -468,6 +472,7 @@ void queryMasterServer(U8 flags, const char* gameType, const char* missionType, sActiveFilter.buddyCount = buddyCount; dFree(sActiveFilter.buddyList); sActiveFilter.buddyList = NULL; + queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots, regionMask, maxPing, minCPU, filterFlags, false, false); } else { @@ -494,28 +499,31 @@ void queryMasterServer(U8 flags, const char* gameType, const char* missionType, processMasterServerQuery(gPingSession); } -ConsoleFunction(queryMasterServer, void, 11, 11, "queryMasterServer(...);") +ConsoleFunction(queryMasterServer, void, 12, 12, "queryMasterServer(...);") { argc; - U8 flags = dAtoi(argv[1]); + U16 lanPort = dAtoi(argv[1]); + U8 flags = dAtoi(argv[2]); // It's not a good idea to hold onto args, recursive calls to // console exec will trash them. - char* gameType = dStrdup(argv[2]); - char* missionType = dStrdup(argv[3]); - - U8 minPlayers = dAtoi(argv[4]); - U8 maxPlayers = dAtoi(argv[5]); - U8 maxBots = dAtoi(argv[6]); - U32 regionMask = dAtoi(argv[7]); - U32 maxPing = dAtoi(argv[8]); - U16 minCPU = dAtoi(argv[9]); - U8 filterFlags = dAtoi(argv[10]); + char* gameType = dStrdup(argv[3]); + char* missionType = dStrdup(argv[4]); + + U8 minPlayers = dAtoi(argv[5]); + U8 maxPlayers = dAtoi(argv[6]); + U8 maxBots = dAtoi(argv[7]); + U32 regionMask = dAtoi(argv[8]); + U32 maxPing = dAtoi(argv[9]); + U16 minCPU = dAtoi(argv[10]); + U8 filterFlags = dAtoi(argv[11]); U8 buddyCount = 0; U32 buddyList = 0; - queryMasterServer(flags, gameType, missionType, minPlayers, maxPlayers, + clearServerList(); + + queryMasterServer(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots, regionMask, maxPing, minCPU, filterFlags, 0, &buddyList); dFree(gameType); @@ -536,7 +544,7 @@ static void sendMasterArrangedConnectRequest(NetAddress* address) // Send a request to the master server to set up an arranged connection: BitStream* out = BitStream::getPacketStream(); - out->write(U8(NetInterface::MasterServerArrangedConnectRequest)); + out->write(U8(NetInterface::MasterServerRequestArrangedConnection)); //char addr[256]; //Net::addressToString(address, addr); @@ -546,26 +554,77 @@ static void sendMasterArrangedConnectRequest(NetAddress* address) out->write(address->netNum[1]); out->write(address->netNum[2]); out->write(address->netNum[3]); - //out->write(address->port); + out->write(address->port); BitStream::sendPacketStream(&(*masterList)[i].address); } } -ConsoleFunction(arrangeConnection, void, 2, 2, "arrangeConnection(ip);") +NetConnection* arrangeNetConnection = NULL; + +ConsoleMethod(NetConnection, arrangeConnection, void, 3, 3, "NetConnection.arrangeConnection(ip);") { + arrangeNetConnection = object; argc; NetAddress addr; char* addrText; - addrText = dStrdup(argv[1]); + addrText = dStrdup(argv[2]); Net::stringToAddress(addrText, &addr); + if (!dStrchr(addrText, ':')) + addr.port = 0; + + dFree(addrText); + + ConnectionParameters& params = arrangeNetConnection->getConnectionParameters(); + params.mToConnectAddress = addr; + sendMasterArrangedConnectRequest(&addr); } + +NetConnection* relayNetConnection = NULL; +static void getRelayServer(const NetAddress* address); + +ConsoleMethod(NetConnection, relayConnection, void, 3, 3, "NetConnection.relayConnection(ip);") +{ + relayNetConnection = object; + argc; + + NetAddress addr; + char* addrText; + + addrText = dStrdup(argv[2]); + + Net::stringToAddress(addrText, &addr); + + if (!dStrchr(addrText, ':')) + addr.port = 0; + + dFree(addrText); + + getRelayServer(&addr); +} #endif +ConsoleFunction(isLocalAddress, bool, 2, 2, "isLocalAddress(addr);") +{ + NetAddress addr; + Net::stringToAddress(argv[1], &addr); + + bool found = false; + for (U32 i = 0; i < localNetAddresses.size(); i++) + { + if (Net::compareAddresses(&localNetAddresses[i], &addr)) + { + found = true; + break; + } + } + return found; +} + //----------------------------------------------------------------------------- ConsoleFunction(querySingleServer, void, 3, 3, "querySingleServer(address, flags);") @@ -580,6 +639,9 @@ ConsoleFunction(querySingleServer, void, 3, 3, "querySingleServer(address, flags Net::stringToAddress(addrText, &addr); + + dFree(addrText); + querySingleServer(&addr, flags); } @@ -748,6 +810,7 @@ ConsoleFunction(setServerInfo, bool, 2, 2, "setServerInfo(index);") Con::setBoolVariable("ServerInfo::Favorite", info.isFavorite); Con::setBoolVariable("ServerInfo::Dedicated", info.isDedicated()); Con::setBoolVariable("ServerInfo::Password", info.isPassworded()); + Con::setBoolVariable("ServerInfo::IsLocal", info.isLocal); return true; } return false; @@ -867,6 +930,7 @@ void clearServerList(bool clearServerInfo) gPingList.clear(); gQueryList.clear(); gServerPingCount = gServerQueryCount = 0; + localNetAddresses.clear(); gPingSession++; } @@ -901,6 +965,7 @@ static void pushPingRequest(const NetAddress* addr) p.time = 0; p.tryCount = gPingRetryCount; p.broadcast = false; + p.isLocal = false; gPingList.push_back(p); gServerPingCount++; } @@ -919,6 +984,7 @@ static void pushPingBroadcast(const NetAddress* addr) p.time = 0; p.tryCount = 1; // only try this once p.broadcast = true; + p.isLocal = true; gPingList.push_back(p); // Don't increment gServerPingCount, broadcasts are not // counted as requests. @@ -1041,6 +1107,25 @@ static void removeServerInfo(const NetAddress* addr) //----------------------------------------------------------------------------- +static void addLocalAddress(const NetAddress* addr) +{ + bool found = false; + for (U32 i = 0; i < localNetAddresses.size(); i++) + { + if (Net::compareAddresses(addr, &localNetAddresses[i])) + { + found = true; + break; + } + } + if (!found) + { + localNetAddresses.push_back(*addr); + } +} + +//----------------------------------------------------------------------------- + #if defined(TORQUE_DEBUG) // This function is solely for testing the functionality of the server browser // with more servers in the list. @@ -1199,7 +1284,8 @@ static void processMasterServerQuery(U32 session) out->write(sActiveFilter.minPlayers); out->write(sActiveFilter.maxPlayers); out->write(sActiveFilter.regionMask); - U32 version = (sActiveFilter.filterFlags & ServerFilter::CurrentVersion) ? getVersionNumber() : 0; + //U32 version = (sActiveFilter.filterFlags & ServerFilter::CurrentVersion) ? getVersionNumber() : 0; + U32 version = getVersionNumber(); out->write(version); out->write(sActiveFilter.filterFlags); out->write(sActiveFilter.maxBots); @@ -1224,7 +1310,8 @@ static void processMasterServerQuery(U32 session) else { Con::errorf("There are no more master servers to try!"); - Con::executef(4, "onServerQueryStatus", "done", "No master servers found.", "0"); + // Con::executef(4, "onServerQueryStatus", "done", "No master servers found.", "0"); + processPingsAndQueries(gPingSession); // Do the LAN ping query?? } } } @@ -1285,6 +1372,23 @@ static void processPingsAndQueries(U32 session, bool schedule) else Con::printf("Pinging Server %s (%d)...", addressString, p.tryCount); sendPacket(NetInterface::GamePingRequest, &p.address, p.key, p.session, flags); + +#ifdef TORQUE_NET_HOLEPUNCHING + if (!p.broadcast) { + BitStream* out = BitStream::getPacketStream(); + out->write(U8(NetInterface::MasterServerGamePingRequest)); + out->write(p.address.netNum[0]); + out->write(p.address.netNum[1]); + out->write(p.address.netNum[2]); + out->write(p.address.netNum[3]); + out->write(p.address.port); + out->write(flags); + out->write((p.session << 16) | (p.key & 0xFFFF)); + for (int i = 0; i < gMasterServerList.size(); i++) + BitStream::sendPacketStream(&gMasterServerList[i].address); + } +#endif + i++; } } @@ -1325,6 +1429,24 @@ static void processPingsAndQueries(U32 session, bool schedule) Con::printf("Querying Server %s (%d)...", addressString, p.tryCount); sendPacket(NetInterface::GameInfoRequest, &p.address, p.key, p.session, flags); + +#ifdef TORQUE_NET_HOLEPUNCHING + if (!p.broadcast) { + BitStream* out = BitStream::getPacketStream(); + out->write(U8(NetInterface::MasterServerGameInfoRequest)); + out->write(p.address.netNum[0]); + out->write(p.address.netNum[1]); + out->write(p.address.netNum[2]); + out->write(p.address.netNum[3]); + out->write(p.address.port); + out->write(flags); + out->write((p.session << 16) | (p.key & 0xFFFF)); + + for (int i = 0; i < gMasterServerList.size(); i++) + BitStream::sendPacketStream(&gMasterServerList[i].address); + } +#endif + if (!si->isQuerying()) { si->status |= ServerInfo::Status_Querying; @@ -1507,7 +1629,7 @@ static void handleMasterServerGameTypesResponse(BitStream* stream, U32 /*key*/, //----------------------------------------------------------------------------- -static void handleMasterServerListResponse(BitStream* stream, U32 key, U8 /*flags*/) +static void handleMasterServerListResponse(BitStream* stream, U32 key, U8 flags) { U8 packetIndex, packetTotal; U32 i; @@ -1551,6 +1673,13 @@ static void handleMasterServerListResponse(BitStream* stream, U32 key, U8 /*flag dSprintf(addressBuffer, sizeof(addressBuffer), "IP:%d.%d.%d.%d:%d", netNum[0], netNum[1], netNum[2], netNum[3], port); Net::stringToAddress(addressBuffer, &addr); + + if (flags) + { + // This is *our* own public IP + addLocalAddress(&addr); + } + pushPingRequest(&addr); } @@ -1621,7 +1750,10 @@ static void handleGameMasterInfoRequest(const NetAddress* address, U32 key, U8 f writeCString(out, Con::getVariable("Server::GameType")); writeCString(out, Con::getVariable("Server::MissionType")); + writeCString(out, Con::getVariable("Server::InviteCode")); + temp8 = U8(Con::getIntVariable("Pref::Server::MaxPlayers")); + temp8 -= U8(Con::getIntVariable("Pref::Server::PrivateSlots")); // Actual count out->write(temp8); temp32 = Con::getIntVariable("Server::RegionMask");//"Pref::Server::RegionMask"); out->write(temp32); @@ -1635,6 +1767,8 @@ static void handleGameMasterInfoRequest(const NetAddress* address, U32 key, U8 f temp8 |= ServerInfo::Status_Dedicated; if (dStrlen(Con::getVariable("Pref::Server::Password")) > 0) temp8 |= ServerInfo::Status_Passworded; + if (Con::getBoolVariable("Server::IsPrivate")) + temp8 |= ServerInfo::Status_Private; out->write(temp8); temp8 = U8(Con::getIntVariable("Server::BotCount")); out->write(temp8); @@ -1679,6 +1813,12 @@ static void handleGamePingRequest(const NetAddress* address, U32 key, U8 flags) if (flags & ServerFilter::OfflineQuery) return; + int maxCount = Con::getIntVariable("Pref::Server::MaxPlayers"); + maxCount -= Con::getIntVariable("Pref::Server::PrivateSlots"); // Actual count + + if (Con::getIntVariable("Server::PlayerCount") >= maxCount) + return; // Don't reply + // some banning code here (?) BitStream* out = BitStream::getPacketStream(); @@ -1720,8 +1860,11 @@ static void handleGamePingResponse(const NetAddress* address, BitStream* stream, { // an anonymous ping response - if it's not already timed // out or finished, ping it. Probably from a broadcast - if (!addressFinished(address)) + if (!addressFinished(address)) { pushPingRequest(address); + S32 index = findPingEntry(gPingList, address); + gPingList[index].isLocal = true; + } return; } Ping& p = gPingList[index]; @@ -1813,9 +1956,10 @@ static void handleGamePingResponse(const NetAddress* address, BitStream* stream, // Get the server build version: stream->read(&temp32); - if (applyFilter - && (sActiveFilter.filterFlags & ServerFilter::CurrentVersion) - && (temp32 != getVersionNumber())) +// if (applyFilter +// && (sActiveFilter.filterFlags & ServerFilter::CurrentVersion) +// && (temp32 != getVersionNumber())) + if (temp32 != getVersionNumber()) { Con::printf("Server %s filtered out by version number.", addrString); gFinishedList.push_back(*address); @@ -1832,6 +1976,7 @@ static void handleGamePingResponse(const NetAddress* address, BitStream* stream, si = findOrCreateServerInfo(address); si->ping = ping; si->version = temp32; + si->isLocal = p.isLocal; // Get the server name: stream->readString(buf); @@ -2060,87 +2205,188 @@ static void handleGameInfoResponse(const NetAddress* address, BitStream* stream, } #ifdef TORQUE_NET_HOLEPUNCHING -static void handleMasterServerArrangedConnectResponse(BitStream* stream, U32 /*key*/, U8 /*flags*/) -{ - Con::printf("Received arranged connect response from the master server."); - // TODO: If not hosting then reject the connection +static char* joinGameAcceptCb = NULL; +static char* joinGameRejectCb = NULL; - // TODO: Implement arranged connection +static void joinGameByInvite(const char* inviteCode) +{ + BitStream* stream = BitStream::getPacketStream(); + stream->write(U8(NetInterface::MasterServerJoinInvite)); + writeCString(stream, inviteCode); - /*if(!gIsServer || Random::readF() > 0.75) - { - // We reject connections about 75% of the time... + Vector* serverList = getMasterServerList(); - logprintf("Rejecting arranged connection from %s", Address(possibleAddresses[0]).toString()); - c2mRejectArrangedConnection(requestId, connectionParameters); - } - else + for (int i = 0; i < serverList->size(); i++) { - // Ok, let's do the arranged connection! + BitStream::sendPacketStream(&(*serverList)[i].address); + } - U8 data[Nonce::NonceSize * 2 + SymmetricCipher::KeySize * 2]; - Random::read(data, sizeof(data)); - IPAddress localAddress = getInterface()->getFirstBoundInterfaceAddress().toIPAddress(); + int netPort = Con::getIntVariable("pref::Server::Port"); - ByteBufferPtr b = new ByteBuffer(data, sizeof(data)); - b->takeOwnership(); - c2mAcceptArrangedConnection(requestId, localAddress, b); - GameConnection *conn = new GameConnection(); + // Now for LAN + stream = BitStream::getPacketStream(); + stream->write(U8(NetInterface::MasterServerJoinInvite)); + U8 flags = 0; + U32 key = 0; - Vector
fullPossibleAddresses; - for(S32 i = 0; i < possibleAddresses.size(); i++) - fullPossibleAddresses.push_back(Address(possibleAddresses[i])); + stream->write(flags); + stream->write(key); + writeCString(stream, inviteCode); - logprintf("Accepting arranged connection from %s", Address(fullPossibleAddresses[0]).toString()); - logprintf(" Generated shared secret data: %s", b->encodeBase64()->getBuffer()); + NetAddress addr; + char addrText[256]; + dSprintf(addrText, sizeof(addrText), "IP:BROADCAST:%d", netPort); + Net::stringToAddress(addrText, &addr); - ByteBufferPtr theSharedData = new ByteBuffer(data + 2 * Nonce::NonceSize, sizeof(data) - 2 * Nonce::NonceSize); - theSharedData->takeOwnership(); - Nonce nonce(data); - Nonce serverNonce(data + Nonce::NonceSize); + BitStream::sendPacketStream(&addr); +} - conn->connectArranged(getInterface(), fullPossibleAddresses, - nonce, serverNonce, theSharedData,false); - }*/ +ConsoleFunction(joinGameByInvite, void, 4, 4, "joinGameByInvite(inviteCode, acceptCb(%ip), rejectCb)") +{ + if (joinGameAcceptCb) + dFree(joinGameAcceptCb); + if (joinGameRejectCb) + dFree(joinGameRejectCb); + + joinGameAcceptCb = dStrdup(argv[2]); + joinGameRejectCb = dStrdup(argv[3]); + joinGameByInvite(argv[1]); } -static void handleMasterServerAcceptArrangedConnectResponse(BitStream* stream, U32 /*key*/, U8 /*flags*/) +static void getRelayServer(const NetAddress* address) { - Con::printf("Received accept arranged connect response from the master server."); + BitStream* stream = BitStream::getPacketStream(); + stream->write(U8(NetInterface::MasterServerRelayRequest)); + stream->write(address->netNum[0]); + stream->write(address->netNum[1]); + stream->write(address->netNum[2]); + stream->write(address->netNum[3]); + stream->write(address->port); - // TODO: Implement accepted arranged connection + Vector* serverList = getMasterServerList(); - /*if(!gIsServer && requestId == mCurrentQueryId && connectionData->getBufferSize() >= Nonce::NonceSize * 2 + SymmetricCipher::KeySize * 2) + for (int i = 0; i < serverList->size(); i++) { - logprintf("Remote host accepted arranged connection."); - logprintf(" Shared secret data: %s", connectionData->encodeBase64()->getBuffer()); - GameConnection *conn = new GameConnection(); + BitStream::sendPacketStream(&(*serverList)[i].address); + } +} + +static void handleMasterServerRelayResponse(const NetAddress* address, BitStream* stream) +{ + Con::printf("Received MasterServerRelayResponse"); + + bool isHost; + stream->read(&isHost); + + NetAddress theAddress; + theAddress.type = NetAddress::IPAddress; + stream->read(&theAddress.netNum[0]); + stream->read(&theAddress.netNum[1]); + stream->read(&theAddress.netNum[2]); + stream->read(&theAddress.netNum[3]); + stream->read(&theAddress.port); + + // Attempt connection to relay + BitStream* out = BitStream::getPacketStream(); + out->write(isHost); + BitStream::sendPacketStream(&theAddress); - Vector
fullPossibleAddresses; - for(S32 i = 0; i < possibleAddresses.size(); i++) - fullPossibleAddresses.push_back(Address(possibleAddresses[i])); + // relayNetConnection->connect(&theAddress); +} - ByteBufferPtr theSharedData = - new ByteBuffer( - (U8 *) connectionData->getBuffer() + Nonce::NonceSize * 2, - connectionData->getBufferSize() - Nonce::NonceSize * 2 - ); - theSharedData->takeOwnership(); +static void handleMasterServerRelayReady(const NetAddress* address) +{ + // Connect to it! + if (relayNetConnection) + GNet->startRelayConnection(relayNetConnection, address); + else if (arrangeNetConnection) + GNet->startRelayConnection(arrangeNetConnection, address); +} - Nonce nonce(connectionData->getBuffer()); - Nonce serverNonce(connectionData->getBuffer() + Nonce::NonceSize); +static void handleMasterServerClientRequestedArrangedConnection(const NetAddress* address, BitStream* stream, U32 /*key*/, U8 /*flags*/) +{ + Con::printf("Received MasterServerClientRequestedArrangedConnection"); + Vector possibleAddresses; + + U16 clientId; + stream->read(&clientId); + + U8 possibleAddressCount; + stream->read(&possibleAddressCount); + for (int i = 0; i < possibleAddressCount; i++) { + U8 ipbits[4]; + U16 port; + stream->read(&ipbits[0]); + stream->read(&ipbits[1]); + stream->read(&ipbits[2]); + stream->read(&ipbits[3]); + stream->read(&port); + NetAddress addr; + addr.type = NetAddress::IPAddress; + addr.port = port; + addr.netNum[0] = ipbits[0]; + addr.netNum[1] = ipbits[1]; + addr.netNum[2] = ipbits[2]; + addr.netNum[3] = ipbits[3]; + possibleAddresses.push_back(addr); + } - conn->connectArranged(getInterface(), fullPossibleAddresses, - nonce, serverNonce, theSharedData,true); - }*/ + BitStream* out = BitStream::getPacketStream(); + out->write(U8(NetInterface::MasterServerAcceptArrangedConnection)); + out->write(clientId); + BitStream::sendPacketStream(address); + + // Do connectArranged to client + NetConnection* conn = dynamic_cast(Sim::findObject("ServerConnection")); + if (conn != NULL) { + conn->connectArranged(possibleAddresses, false); + } +} + +static void handleMasterServerArrangedConnectionAccepted(const NetAddress* address, BitStream* stream, U32 /*key*/, U8 /*flags*/) +{ + Vector possibleAddresses; + + Con::printf("Received accept arranged connect response from the master server."); + + U8 possibleAddressCount; + stream->read(&possibleAddressCount); + for (int i = 0; i < possibleAddressCount; i++) { + U8 ipbits[4]; + U16 port; + stream->read(&ipbits[0]); + stream->read(&ipbits[1]); + stream->read(&ipbits[2]); + stream->read(&ipbits[3]); + stream->read(&port); + NetAddress addr; + addr.type = NetAddress::IPAddress; + addr.port = port; + addr.netNum[0] = ipbits[0]; + addr.netNum[1] = ipbits[1]; + addr.netNum[2] = ipbits[2]; + addr.netNum[3] = ipbits[3]; + possibleAddresses.push_back(addr); + } + + // Do connectArranged to server + arrangeNetConnection->connectArranged(possibleAddresses, true); } -static void handleMasterServerRejectArrangedConnectResponse(BitStream* stream, U32 /*key*/, U8 /*flags*/) +static void handleMasterServerArrangedConnectionRejected(const NetAddress* address, BitStream* stream, U32 /*key*/, U8 /*flags*/) { Con::printf("Received reject arranged connect response from the master server."); + U8 reason; + stream->read(&reason); + + // Reject?? + if (reason == 0) + arrangeNetConnection->onConnectionRejected("No such server"); + if (reason == 1) + arrangeNetConnection->onConnectionRejected("Server rejected"); // TODO: Implement rejected arranged connection /*if(!gIsServer && requestId == mCurrentQueryId) @@ -2150,6 +2396,117 @@ static void handleMasterServerRejectArrangedConnectResponse(BitStream* stream, U startGameTypesQuery(); }*/ } + +static void handleMasterServerGamePingResponse(const NetAddress* address, BitStream* stream) { + NetAddress theAddress; + theAddress.type = NetAddress::IPAddress; + stream->read(&theAddress.netNum[0]); + stream->read(&theAddress.netNum[1]); + stream->read(&theAddress.netNum[2]); + stream->read(&theAddress.netNum[3]); + stream->read(&theAddress.port); + U8 cmd; + stream->read(&cmd); + U8 flags; + U32 key; + + stream->read(&flags); + stream->read(&key); + handleGamePingResponse(&theAddress, stream, key, flags); +} + +static void handleMasterServerGameInfoResponse(const NetAddress* address, BitStream* stream) { + NetAddress theAddress; + theAddress.type = NetAddress::IPAddress; + stream->read(&theAddress.netNum[0]); + stream->read(&theAddress.netNum[1]); + stream->read(&theAddress.netNum[2]); + stream->read(&theAddress.netNum[3]); + stream->read(&theAddress.port); + U8 cmd; + stream->read(&cmd); + U8 flags; + U32 key; + + stream->read(&flags); + stream->read(&key); + handleGameInfoResponse(&theAddress, stream, key, flags); +} + +static void handleMasterServerJoinInvite(const NetAddress* address, BitStream* stream) { + char inv[32]; + readCString(stream, (char*) &inv); + const char* ourInv = Con::getVariable("Server::InviteCode"); + if (strcmp(ourInv, inv) == 0) { + // RESPOND + U16 netPort = Con::getIntVariable("pref::Server::Port"); + + BitStream* stream = BitStream::getPacketStream(); + stream->write(U8(NetInterface::MasterServerJoinInviteResponse)); + U8 flags = 0; + U32 key = 0; + U8 found = 1; + + stream->write(flags); + stream->write(key); + + stream->write(found); + + // We just replace the netNum with 255.255.255.255 and filter that out on client side + NetAddress theAddress; + theAddress.netNum[0] = 255; + theAddress.netNum[1] = 255; + theAddress.netNum[2] = 255; + theAddress.netNum[3] = 255; + stream->write(theAddress.netNum[0]); + stream->write(theAddress.netNum[1]); + stream->write(theAddress.netNum[2]); + stream->write(theAddress.netNum[3]); + stream->write(netPort); + + BitStream::sendPacketStream(address); + } +} + +static void handleMasterServerJoinInviteResponse(const NetAddress* address, BitStream* stream) { + U8 found = true; + stream->read(&found); + if (found) + { + NetAddress theAddress; + theAddress.type = NetAddress::IPAddress; + stream->read(&theAddress.netNum[0]); + stream->read(&theAddress.netNum[1]); + stream->read(&theAddress.netNum[2]); + stream->read(&theAddress.netNum[3]); + stream->read(&theAddress.port); + + bool isLocal = false; + if (theAddress.netNum[0] == 255 && theAddress.netNum[1] == 255 && theAddress.netNum[2] == 255 && theAddress.netNum[3] == 255) { + theAddress.netNum[0] = address->netNum[0]; + theAddress.netNum[1] = address->netNum[1]; + theAddress.netNum[2] = address->netNum[2]; + theAddress.netNum[3] = address->netNum[3]; + + isLocal = true; + } + + + char evalbuf[128]; + dSprintf(evalbuf, 128, "%s(\"%d.%d.%d.%d:%d\",%s);", joinGameAcceptCb, theAddress.netNum[0], theAddress.netNum[1], theAddress.netNum[2], theAddress.netNum[3], theAddress.port, isLocal ? "true" : "false"); + Con::evaluatef(evalbuf); + } + else + { + char evalbuf[64]; + dSprintf(evalbuf, 64, "%s();", joinGameRejectCb); + Con::evaluatef(evalbuf); + } + //dFree(joinGameAcceptCb); + //dFree(joinGameRejectCb); + //joinGameAcceptCb = NULL; + //joinGameRejectCb = NULL; +} #endif //----------------------------------------------------------------------------- @@ -2194,14 +2551,32 @@ void DemoNetInterface::handleInfoPacket(const NetAddress* address, U8 packetType break; #ifdef TORQUE_NET_HOLEPUNCHING - case MasterServerArrangedConnectResponse: - handleMasterServerArrangedConnectResponse(stream, key, flags); + case MasterServerClientRequestedArrangedConnection: + handleMasterServerClientRequestedArrangedConnection(address, stream, key, flags); + break; + case MasterServerArrangedConnectionAccepted: + handleMasterServerArrangedConnectionAccepted(address, stream, key, flags); + break; + case MasterServerArrangedConnectionRejected: + handleMasterServerArrangedConnectionRejected(address, stream, key, flags); + break; + case MasterServerGamePingResponse: + handleMasterServerGamePingResponse(address, stream); + break; + case MasterServerGameInfoResponse: + handleMasterServerGameInfoResponse(address, stream); + break; + case MasterServerRelayResponse: + handleMasterServerRelayResponse(address, stream); + break; + case MasterServerRelayReady: + handleMasterServerRelayReady(address); break; - case MasterServerAcceptArrangedConnectResponse: - handleMasterServerAcceptArrangedConnectResponse(stream, key, flags); + case MasterServerJoinInvite: + handleMasterServerJoinInvite(address, stream); break; - case MasterServerRejectArrangedConnectResponse: - handleMasterServerRejectArrangedConnectResponse(stream, key, flags); + case MasterServerJoinInviteResponse: + handleMasterServerJoinInviteResponse(address, stream); break; #endif } diff --git a/engine/source/game/net/serverQuery.h b/engine/source/game/net/serverQuery.h index 5d781c5a..f3c3138b 100644 --- a/engine/source/game/net/serverQuery.h +++ b/engine/source/game/net/serverQuery.h @@ -45,6 +45,7 @@ struct ServerInfo Status_Dedicated = BIT(0), Status_Passworded = BIT(1), Status_Linux = BIT(2), + Status_Private = BIT(3), // Status flags: Status_New = 0, @@ -69,6 +70,7 @@ struct ServerInfo U32 cpuSpeed; bool isFavorite; BitSet32 status; + bool isLocal; ServerInfo() { @@ -86,6 +88,7 @@ struct ServerInfo cpuSpeed = 0; isFavorite = false; status = Status_New; + isLocal = false; } ~ServerInfo(); @@ -105,18 +108,20 @@ struct ServerInfo extern Vector gServerList; extern bool gServerBrowserDirty; +extern NetConnection* relayNetConnection; extern void clearServerList(bool clearServerInfo = true); extern void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType, U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, U8 filterFlags, bool clearServerInfo, bool useFilters); extern void queryMasterGameTypes(); -extern void queryMasterServer(U8 flags, const char* gameType, const char* missionType, +extern void queryMasterServer(U16 port, U8 flags, const char* gameType, const char* missionType, U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, U8 filterFlags, U8 buddyCount, U32* buddyList); extern void queryFavoriteServers(U8 flags); extern void querySingleServer(const NetAddress* addr, U8 flags); extern void startHeartbeat(); extern void sendHeartbeat(U8 flags); +extern Vector* getMasterServerList(); #ifdef TORQUE_DEBUG extern void addFakeServers(S32 howMany); diff --git a/engine/source/gui/controls/guiXboxButtonCtrl.cpp b/engine/source/gui/controls/guiXboxButtonCtrl.cpp index 9f10827f..b6c53984 100644 --- a/engine/source/gui/controls/guiXboxButtonCtrl.cpp +++ b/engine/source/gui/controls/guiXboxButtonCtrl.cpp @@ -89,6 +89,12 @@ ConsoleMethod(GuiXboxButtonCtrl, getText, const char*, 2, 2, "() - returns the t return object->getText(); } +ConsoleMethod(GuiXboxButtonCtrl, setHover, void, 3, 3, "(hover) - sets the button hover state.") +{ + argc; argv; + object->setButtonHover(atoi(argv[2])); +} + //-------------------------------------------------------- // Misc //-------------------------------------------------------- @@ -104,6 +110,20 @@ ConsoleMethod(GuiXboxButtonCtrl, getText, const char*, 2, 2, "() - returns the t // } //} +void GuiXboxButtonCtrl::setButtonHover(bool hover) +{ + if (hover) + { + mButtonState = Hover; + mHovering = true; + } + else + { + mButtonState = Normal; + mHovering = false; + } +} + void GuiXboxButtonCtrl::setText(const char* text) { // Hacky workaround to make hover not persist between guis diff --git a/engine/source/gui/controls/guiXboxButtonCtrl.h b/engine/source/gui/controls/guiXboxButtonCtrl.h index 0441f005..abefe997 100644 --- a/engine/source/gui/controls/guiXboxButtonCtrl.h +++ b/engine/source/gui/controls/guiXboxButtonCtrl.h @@ -32,6 +32,7 @@ class GuiXboxButtonCtrl : public GuiControl static void initPersistFields(); //void setVisible(bool value) override; + void setButtonHover(bool hover); void setText(const char* text); void setTextID(S32 id); void setTextID(const char* id); diff --git a/engine/source/gui/controls/guiXboxOptionListCtrl.cpp b/engine/source/gui/controls/guiXboxOptionListCtrl.cpp index 2b10dd65..47c82038 100644 --- a/engine/source/gui/controls/guiXboxOptionListCtrl.cpp +++ b/engine/source/gui/controls/guiXboxOptionListCtrl.cpp @@ -500,7 +500,8 @@ void GuiXboxOptionListCtrl::onRender(Point2I offset, const RectI& updateRect) rect.extent.set(bitmapArrowWidth, bitmapArrowHeight); } - GFX->drawBitmapStretchSR(mProfile->mTextureObject, rect, mProfile->mBitmapArrayRects[leftArrowIndex]); + if (mButtonsEnabled) + GFX->drawBitmapStretchSR(mProfile->mTextureObject, rect, mProfile->mBitmapArrayRects[leftArrowIndex]); } S32 rightArrowIndex = unselectedRightArrowIndex; @@ -563,7 +564,8 @@ void GuiXboxOptionListCtrl::onRender(Point2I offset, const RectI& updateRect) rect.extent.set(bitmapArrowWidth, bitmapArrowHeight); } - GFX->drawBitmapStretchSR(mProfile->mTextureObject, rect, mProfile->mBitmapArrayRects[rightArrowIndex]); + if (mButtonsEnabled) + GFX->drawBitmapStretchSR(mProfile->mTextureObject, rect, mProfile->mBitmapArrayRects[rightArrowIndex]); } #ifndef MBO_UNTOUCHED_MENUS diff --git a/engine/source/sim/netConnection.cpp b/engine/source/sim/netConnection.cpp index 4a944fef..894bd020 100644 --- a/engine/source/sim/netConnection.cpp +++ b/engine/source/sim/netConnection.cpp @@ -1032,7 +1032,7 @@ void NetConnection::connect(const NetAddress* address) } #ifdef TORQUE_NET_HOLEPUNCHING -void NetConnection::connectArranged(const Vector &possibleAddresses, bool isInitiator) +void NetConnection::connectArranged(const Vector &possibleAddresses, bool isInitiator) { //mConnectionParameters.mRequestKeyExchange = requestsKeyExchange; //mConnectionParameters.mRequestCertificate = requestsCertificate; diff --git a/engine/source/sim/netConnection.h b/engine/source/sim/netConnection.h index b122cc31..08ec62cc 100644 --- a/engine/source/sim/netConnection.h +++ b/engine/source/sim/netConnection.h @@ -322,11 +322,12 @@ struct ConnectionParameters //bool mRequestCertificate; ///< The client is requesting a certificate //U8 mSymmetricKey[SymmetricCipher::KeySize]; ///< The symmetric key for the connection, generated by the client //U8 mInitVector[SymmetricCipher::KeySize]; ///< The init vector, generated by the server - Vector mPossibleAddresses; ///< List of possible addresses for the remote host in an arranged connection. + Vector mPossibleAddresses; ///< List of possible addresses for the remote host in an arranged connection. bool mIsInitiator; ///< True if this host initiated the arranged connection. //bool mIsLocal; ///< True if this is a connectLocal connection. //ByteBufferPtr mArrangedSecret; ///< The shared secret as arranged by the connection intermediary. bool mDebugObjectSizes; ///< This connection's initiator requested debugging size information during packet writes. + NetAddress mToConnectAddress; ConnectionParameters() { @@ -459,7 +460,7 @@ class NetConnection : public ConnectionProtocol, public SimGroup void connect(const NetAddress* address); #ifdef TORQUE_NET_HOLEPUNCHING - void connectArranged(const Vector &possibleAddresses, bool isInitiator); + void connectArranged(const Vector &possibleAddresses, bool isInitiator); #endif //---------------------------------------------------------------- @@ -659,6 +660,7 @@ class NetConnection : public ConnectionProtocol, public SimGroup Connected, ///< We've accepted a connect request, or we've received a connect response accept. #ifdef TORQUE_NET_HOLEPUNCHING SendingPunchPackets, + TryingRelay #endif // TORQUE_NET_HOLEPUNCHING }; diff --git a/engine/source/sim/netInterface.cpp b/engine/source/sim/netInterface.cpp index 1f67354a..12c406df 100644 --- a/engine/source/sim/netInterface.cpp +++ b/engine/source/sim/netInterface.cpp @@ -15,6 +15,7 @@ #include "GGCNatTunnel.h" extern void HandleGGCPacket(NetAddress* addr, unsigned char* data, U32 dataSize); #endif +#include NetInterface* GNet = NULL; @@ -95,8 +96,11 @@ void NetInterface::processPacketReceiveEvent(PacketReceiveEvent* prEvent) U8 packetType; pStream.read(&packetType); NetAddress* addr = &prEvent->sourceAddress; - +#ifdef TORQUE_NET_HOLEPUNCHING + if (packetType <= GameHeartbeat || packetType >= MasterServerRequestArrangedConnection) +#else if (packetType <= GameHeartbeat) +#endif handleInfoPacket(addr, packetType, &pStream); #ifdef GGC_PLUGIN else if (packetType == GGCPacket) @@ -427,14 +431,45 @@ void NetInterface::handleConnectReject(const NetAddress* address, BitStream* str // NetInterface arranged connection process //----------------------------------------------------------------------------- +void NetInterface::sendRelayPackets(NetConnection* conn) +{ + Con::executef(conn, 2, "onConnectStatus", Con::getIntArg(4)); + relayNetConnection = conn; + BitStream* out = BitStream::getPacketStream(); + + ConnectionParameters& params = conn->getConnectionParameters(); + + out->write(U8(NetInterface::MasterServerRelayRequest)); + out->write(params.mToConnectAddress.netNum[0]); + out->write(params.mToConnectAddress.netNum[1]); + out->write(params.mToConnectAddress.netNum[2]); + out->write(params.mToConnectAddress.netNum[3]); + out->write(params.mToConnectAddress.port); + + Vector* serverList = getMasterServerList(); + + for (int i = 0; i < serverList->size(); i++) + { + BitStream::sendPacketStream(&(*serverList)[i].address); + } + + conn->mConnectSendCount++; + conn->mConnectLastSendTime = Platform::getVirtualMilliseconds(); //getCurrentTime(); +} + +void NetInterface::startRelayConnection(NetConnection* conn, const NetAddress* theAddress) +{ + conn->setConnectionState(NetConnection::NotConnected); + removePendingConnection(conn); // Remove this pls + + conn->connect(theAddress); // Just do it *normally* +} + void NetInterface::startArrangedConnection(NetConnection *conn) { conn->setConnectionState(NetConnection::SendingPunchPackets); addPendingConnection(conn); conn->mConnectSendCount = 0; - - // TODO: Figure out which to use - //conn->setConnectSequence(Platform::getVirtualMilliseconds()); conn->mConnectLastSendTime = Platform::getVirtualMilliseconds(); sendPunchPackets(conn); @@ -442,45 +477,21 @@ void NetInterface::startArrangedConnection(NetConnection *conn) void NetInterface::sendPunchPackets(NetConnection *conn) { - // TODO: The following code is from OpenTNL, it needs to be updated to work with Torque/OpenMBU - + Con::executef(conn, 2, "onConnectStatus", Con::getIntArg(1)); + ConnectionParameters &theParams = conn->getConnectionParameters(); BitStream* out = BitStream::getPacketStream(); - //PacketStream out; out->write(U8(Punch)); - //if(theParams.mIsInitiator) - // theParams.mNonce.write(&out); - //else - // theParams.mServerNonce.write(&out); - - //U32 encryptPos = out.getBytePosition(); - //out->setBytePosition(encryptPos); - - //if(theParams.mIsInitiator) - // theParams.mServerNonce.write(&out); - //else - //{ - // theParams.mNonce.write(&out); -// if(out->writeFlag(mRequiresKeyExchange || (theParams.mRequestKeyExchange && !mPrivateKey.isNull()))) -// { -// if(out->writeFlag(theParams.mRequestCertificate && !mCertificate.isNull())) -// out->write(mCertificate); -// else -// out->write(mPrivateKey->getPublicKey()); -// } - //} - //SymmetricCipher theCipher(theParams.mArrangedSecret); - //out->hashAndEncrypt(NetConnection::MessageSignatureBytes, encryptPos, &theCipher); - for(S32 i = 0; i < theParams.mPossibleAddresses.size(); i++) { - BitStream::sendPacketStream(theParams.mPossibleAddresses[i]); - -// TNLLogMessageV(LogNetInterface, ("Sending punch packet (%s, %s) to %s", -// ByteBuffer(theParams.mNonce.data, Nonce::NonceSize).encodeBase64()->getBuffer(), -// ByteBuffer(theParams.mServerNonce.data, Nonce::NonceSize).encodeBase64()->getBuffer(), -// theParams.mPossibleAddresses[i].toString())); + BitStream::sendPacketStream(&theParams.mPossibleAddresses[i]); + Con::printf("Sending punch packets to %d.%d.%d.%d:%d", + theParams.mPossibleAddresses[i].netNum[0], + theParams.mPossibleAddresses[i].netNum[1], + theParams.mPossibleAddresses[i].netNum[2], + theParams.mPossibleAddresses[i].netNum[3], + theParams.mPossibleAddresses[i].port); } conn->mConnectSendCount++; conn->mConnectLastSendTime = Platform::getVirtualMilliseconds(); //getCurrentTime(); @@ -488,18 +499,9 @@ void NetInterface::sendPunchPackets(NetConnection *conn) void NetInterface::handlePunch(const NetAddress* theAddress, BitStream *stream) { - // TODO: The following code is from OpenTNL, it needs to be updated to work with Torque/OpenMBU - S32 i, j; NetConnection *conn; - //Nonce firstNonce; - //firstNonce.read(stream); - - //ByteBuffer b(firstNonce.data, Nonce::NonceSize); - - //TNLLogMessageV(LogNetInterface, ("Received punch packet from %s - %s", theAddress.toString(), b.encodeBase64()->getBuffer())); - //Con::printf("Received punch packet from %d.%d.%d.%d:%d", theAddress->netNum[0], theAddress->netNum[1], theAddress->netNum[2], theAddress->netNum[3], theAddress->port); char addr[256]; Net::addressToString(theAddress, addr); Con::printf("Received punch packet from %s", addr); @@ -512,14 +514,10 @@ void NetInterface::handlePunch(const NetAddress* theAddress, BitStream *stream) if(conn->getConnectionState() != NetConnection::SendingPunchPackets) continue; -// if((theParams.mIsInitiator && firstNonce != theParams.mServerNonce) || -// (!theParams.mIsInitiator && firstNonce != theParams.mNonce)) -// continue; - // first see if the address is in the possible addresses list: for(j = 0; j < theParams.mPossibleAddresses.size(); j++) - if(theAddress == theParams.mPossibleAddresses[j]) + if(theAddress == &theParams.mPossibleAddresses[j]) break; // if there was an exact match, just exit the loop, or @@ -538,7 +536,7 @@ void NetInterface::handlePunch(const NetAddress* theAddress, BitStream *stream) // as only the port is not an exact match: for(j = 0; j < theParams.mPossibleAddresses.size(); j++) - if(Net::compareAddresses(theAddress, theParams.mPossibleAddresses[j])) + if(Net::compareAddresses(theAddress, &theParams.mPossibleAddresses[j])) break; // if the address wasn't even partially in the list, just exit out @@ -548,7 +546,7 @@ void NetInterface::handlePunch(const NetAddress* theAddress, BitStream *stream) // otherwise, as long as we don't have too many ping addresses, // add this one to the list: if(theParams.mPossibleAddresses.size() < 5) - theParams.mPossibleAddresses.push_back(theAddress); + theParams.mPossibleAddresses.push_back(*theAddress); // if this is the initiator of the arranged connection, then // process the punch packet from the remote host by issueing a @@ -560,48 +558,12 @@ void NetInterface::handlePunch(const NetAddress* theAddress, BitStream *stream) return; ConnectionParameters &theParams = conn->getConnectionParameters(); -// SymmetricCipher theCipher(theParams.mArrangedSecret); -// if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, stream->getBytePosition(), &theCipher)) -// return; -// -// Nonce nextNonce; -// nextNonce.read(stream); -// -// if(nextNonce != theParams.mNonce) -// return; - - // see if the connection needs to be authenticated or uses key exchange -// if(stream->readFlag()) -// { -// if(stream->readFlag()) -// { -// theParams.mCertificate = new Certificate(stream); -// if(!theParams.mCertificate->isValid() || !conn->validateCertficate(theParams.mCertificate, true)) -// return; -// theParams.mPublicKey = theParams.mCertificate->getPublicKey(); -// } -// else -// { -// theParams.mPublicKey = new AsymmetricKey(stream); -// if(!theParams.mPublicKey->isValid() || !conn->validatePublicKey(theParams.mPublicKey, true)) -// return; -// } -// if(mPrivateKey.isNull() || mPrivateKey->getKeySize() != theParams.mPublicKey->getKeySize()) -// { -// // we don't have a private key, so generate one for this connection -// theParams.mPrivateKey = new AsymmetricKey(theParams.mPublicKey->getKeySize()); -// } -// else -// theParams.mPrivateKey = mPrivateKey; -// theParams.mSharedSecret = theParams.mPrivateKey->computeSharedSecretKey(theParams.mPublicKey); -// //logprintf("shared secret (client) %s", theParams.mSharedSecret->encodeBase64()->getBuffer()); -// Random::read(theParams.mSymmetricKey, SymmetricCipher::KeySize); -// theParams.mUsingCrypto = true; -// } + conn->setNetAddress(theAddress); - //TNLLogMessageV(LogNetInterface, ("Punch from %s matched nonces - connecting...", theAddress.toString())); Con::printf("Punch from %s matched nonces - connecting...", addr); + Con::executef(conn, 2, "onConnectStatus", Con::getIntArg(2)); + conn->setConnectionState(NetConnection::AwaitingConnectResponse); conn->mConnectSendCount = 0; conn->mConnectLastSendTime = Platform::getVirtualMilliseconds();//getCurrentTime(); @@ -611,43 +573,14 @@ void NetInterface::handlePunch(const NetAddress* theAddress, BitStream *stream) void NetInterface::sendArrangedConnectRequest(NetConnection *conn) { - // TODO: The following code is from OpenTNL, it needs to be updated to work with Torque/OpenMBU - - //TNLLogMessageV(LogNetInterface, ("Sending Arranged Connect Request")); Con::printf("Sending Arranged Connect Request"); BitStream* out = BitStream::getPacketStream(); - //PacketStream out; ConnectionParameters &theParams = conn->getConnectionParameters(); out->write(U8(ArrangedConnectRequest)); out->write(conn->getSequence()); - //theParams.mNonce.write(&out); - //U32 encryptPos = out.getBytePosition(); - //U32 innerEncryptPos = 0; - - //out.setBytePosition(encryptPos); - - //theParams.mServerNonce.write(&out); -// if(out.writeFlag(theParams.mUsingCrypto)) -// { -// out.write(theParams.mPrivateKey->getPublicKey()); -// innerEncryptPos = out.getBytePosition(); -// out.setBytePosition(innerEncryptPos); -// out.write(SymmetricCipher::KeySize, theParams.mSymmetricKey); -// } out->writeFlag(theParams.mDebugObjectSizes); - //out->write(conn->getInitialSendSequence()); - out->write(conn->getSequence()); - conn->writeConnectRequest(out); - -// if(innerEncryptPos) -// { -// SymmetricCipher theCipher(theParams.mSharedSecret); -// out.hashAndEncrypt(NetConnection::MessageSignatureBytes, innerEncryptPos, &theCipher); -// } -// SymmetricCipher theCipher(theParams.mArrangedSecret); -// out.hashAndEncrypt(NetConnection::MessageSignatureBytes, encryptPos, &theCipher); conn->mConnectSendCount++; conn->mConnectLastSendTime = Platform::getVirtualMilliseconds();//getCurrentTime(); @@ -658,41 +591,18 @@ void NetInterface::sendArrangedConnectRequest(NetConnection *conn) void NetInterface::handleArrangedConnectRequest(const NetAddress* theAddress, BitStream *stream) { - // TODO: The following code is from OpenTNL, it needs to be updated to work with Torque/OpenMBU - S32 i, j; NetConnection *conn; - //Nonce nonce, serverNonce; - //nonce.read(stream); U32 connectSequence; stream->read(&connectSequence); - // see if the connection is in the main connection table. - // If the connection is in the connection table and it has - // the same initiatorSequence, we'll just resend the connect - // acceptance packet, assuming that the last time we sent it - // it was dropped. - //NetConnection *oldConnection = findConnection(theAddress); - NetConnection* connect = NetConnection::lookup(theAddress); - if (connect && connect->getSequence() == connectSequence) - { - sendConnectAccept(connect); - return; - } - for(i = 0; i < mPendingConnections.size(); i++) { conn = mPendingConnections[i]; ConnectionParameters &theParams = conn->getConnectionParameters(); - if(conn->getConnectionState() != NetConnection::SendingPunchPackets || theParams.mIsInitiator) - continue; - - //if(nonce != theParams.mNonce) - // continue; - for(j = 0; j < theParams.mPossibleAddresses.size(); j++) - if(Net::compareAddresses(theAddress, theParams.mPossibleAddresses[j])) + if(Net::compareAddresses(theAddress, &theParams.mPossibleAddresses[j])) break; if(j != theParams.mPossibleAddresses.size()) break; @@ -701,71 +611,26 @@ void NetInterface::handleArrangedConnectRequest(const NetAddress* theAddress, Bi return; ConnectionParameters &theParams = conn->getConnectionParameters(); -// SymmetricCipher theCipher(theParams.mArrangedSecret); -// if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, stream->getBytePosition(), &theCipher)) -// return; -// -// stream->setBytePosition(stream->getBytePosition()); -// -// serverNonce.read(stream); -// if(serverNonce != theParams.mServerNonce) -// return; - -// if(stream->readFlag()) -// { -// if(mPrivateKey.isNull()) -// return; -// theParams.mUsingCrypto = true; -// theParams.mPublicKey = new AsymmetricKey(stream); -// theParams.mPrivateKey = mPrivateKey; -// -// U32 decryptPos = stream->getBytePosition(); -// stream->setBytePosition(decryptPos); -// theParams.mSharedSecret = theParams.mPrivateKey->computeSharedSecretKey(theParams.mPublicKey); -// SymmetricCipher theCipher(theParams.mSharedSecret); -// -// if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, decryptPos, &theCipher)) -// return; -// -// // now read the first part of the connection's session (symmetric) key -// stream->read(SymmetricCipher::KeySize, theParams.mSymmetricKey); -// Random::read(theParams.mInitVector, SymmetricCipher::KeySize); -// } - //U32 connectSequence; theParams.mDebugObjectSizes = stream->readFlag(); - //stream->read(&connectSequence); - //TNLLogMessageV(LogNetInterface, ("Received Arranged Connect Request")); - Con::printf("Received Arranged Connect Request"); - - if(connect) - { - //connect->disconnect(NetConnection::ReasonSelfDisconnect, ""); - - connect->onDisconnect("SelfDisconnect"); - connect->deleteObject(); + Con::printf("Received Arranged Connect Request: Initiator: %d", theParams.mIsInitiator); + conn->setConnectionState(NetConnection::Connected); + removePendingConnection(conn); + + if (theParams.mIsInitiator) { + Con::executef(conn, 2, "onConnectStatus", Con::getIntArg(3)); + conn->connect(theAddress); } - - conn->setNetAddress(theAddress); - //conn->setInitialRecvSequence(connectSequence); - - conn->setConnectSequence(connectSequence); - //if(theParams.mUsingCrypto) - // conn->setSymmetricCipher(new SymmetricCipher(theParams.mSymmetricKey, theParams.mInitVector)); - - const char *errorString = NULL; - if(!conn->readConnectRequest(stream, &errorString)) + else { - //sendConnectReject(&theParams, theAddress);//, errorString); - sendConnectReject(conn, errorString); - removePendingConnection(conn); - return; + // Send connect request to *client* + BitStream* out = BitStream::getPacketStream(); + + out->write(U8(ArrangedConnectRequest)); + out->write(conn->getSequence()); + + BitStream::sendPacketStream(theAddress); } - //addConnection(conn); - removePendingConnection(conn); - conn->setConnectionState(NetConnection::Connected); - conn->onConnectionEstablished(theParams.mIsInitiator); - sendConnectAccept(conn); } #endif // TORQUE_NET_HOLEPUNCHING @@ -865,6 +730,49 @@ void NetInterface::checkTimeouts() else sendConnectRequest(pending); } +#ifdef TORQUE_NET_HOLEPUNCHING + else if (pending->getConnectionState() == NetConnection::SendingPunchPackets && + time > pending->mConnectLastSendTime + ConnectRetryTime) + { + if (pending->mConnectSendCount > ConnectRetryCount) + { + // Try relay + if (pending->mConnectionParameters.mIsInitiator) { + pending->setConnectionState(NetConnection::TryingRelay); + pending->mConnectSendCount = 0; + pending->mConnectLastSendTime = time; + sendRelayPackets(pending); + } + else + { + pending->setConnectionState(NetConnection::NotConnected); + removePendingConnection(pending); + continue; + } + } + else + sendPunchPackets(pending); + } + else if (pending->getConnectionState() == NetConnection::TryingRelay && + time > pending->mConnectLastSendTime + ConnectRetryTime) + { + if (pending->mConnectSendCount > ConnectRetryCount) + { + if (pending->mConnectionParameters.mIsInitiator) { + pending->onConnectTimedOut(); + pending->deleteObject(); + } + else + { + pending->setConnectionState(NetConnection::NotConnected); + } + removePendingConnection(pending); + continue; + } + else + sendRelayPackets(pending); + } +#endif i++; } mLastTimeoutCheckTime = time; diff --git a/engine/source/sim/netInterface.h b/engine/source/sim/netInterface.h index 09f89308..ae5c6491 100644 --- a/engine/source/sim/netInterface.h +++ b/engine/source/sim/netInterface.h @@ -38,12 +38,21 @@ class NetInterface Punch = 40, ArrangedConnectRequest = 42, - MasterServerArrangedConnectRequest = 46, - MasterServerArrangedConnectResponse = 48, - MasterServerAcceptArrangedConnectRequest = 50, - MasterServerAcceptArrangedConnectResponse = 52, - MasterServerRejectArrangedConnectRequest = 54, - MasterServerRejectArrangedConnectResponse = 56, + MasterServerRequestArrangedConnection = 46, + MasterServerClientRequestedArrangedConnection = 48, + MasterServerAcceptArrangedConnection = 50, + MasterServerArrangedConnectionAccepted = 52, + MasterServerRejectArrangedConnection = 54, + MasterServerArrangedConnectionRejected = 56, + MasterServerGamePingRequest = 58, + MasterServerGamePingResponse = 60, + MasterServerGameInfoRequest = 62, + MasterServerGameInfoResponse = 64, + MasterServerRelayRequest = 66, + MasterServerRelayResponse = 68, + MasterServerRelayReady = 72, + MasterServerJoinInvite = 74, + MasterServerJoinInviteResponse = 76, #endif // TORQUE_NET_HOLEPUNCHING }; protected: @@ -96,6 +105,9 @@ class NetInterface /// Begins the connection handshaking process for an arranged connection. void startArrangedConnection(NetConnection *conn); + /// Begins connecting to the relay server. + void startRelayConnection(NetConnection* conn, const NetAddress* theAddress); + protected: /// Sends Punch packets to each address in the possible connection address list. void sendPunchPackets(NetConnection *conn); @@ -109,6 +121,9 @@ class NetInterface /// Handles an incoming connect request from an arranged connection. void handleArrangedConnectRequest(const NetAddress* theAddress, BitStream *stream); + /// Sends relay requests + void sendRelayPackets(NetConnection* conn); + #endif // TORQUE_NET_HOLEPUNCHING void handleDisconnect(const NetAddress* address, BitStream* stream); diff --git a/game/MBU.torsion.exports b/game/MBU.torsion.exports index aaaf7dd4..28d4eb9d 100644 --- a/game/MBU.torsion.exports +++ b/game/MBU.torsion.exports @@ -3135,6 +3135,10 @@ More of a helper function than anything. If console access to the field list i getText - returns the text of the button. + setHover + hover + - sets the button hover state. + setText string text - sets the text of the button to the string. @@ -3900,6 +3904,9 @@ More of a helper function than anything. If console access to the field list i NetConnection SimGroup + arrangeConnection + ip + checkMaxRate clearPaths @@ -3927,6 +3934,9 @@ More of a helper function than anything. If console access to the field list i getXnAddr + relayConnection + ip + resolveGhostID S32 ghostID Convert a ghost id from this connection to a real id. @@ -5814,6 +5824,9 @@ More of a helper function than anything. If console access to the field list i DRLTarget float DRLTarget + shadowColor + ColorF shadowColor + useBloom bool useBloom @@ -7272,6 +7285,9 @@ Sim time is time since the game started. isKoreanBuild + isLocalAddress + addr + isObject object @@ -7304,6 +7320,10 @@ Sim time is time since the game started. isXInputConnected int controllerID + joinGameByInvite + inviteCode, acceptCb(%ip + , rejectCb) + lightScene script_function completeCallback=NULL, string mode="" Relight the scene. diff --git a/game/common/local/englishStrings.inf b/game/common/local/englishStrings.inf index 40dae33c..3c3417cc 100644 --- a/game/common/local/englishStrings.inf +++ b/game/common/local/englishStrings.inf @@ -230,6 +230,7 @@ $Text::Select = "Select"; $Text::QuickMatch = "Quick Match"; $Text::OptiMatch = "Custom Match"; $Text::CreateMatch = "Create Match"; +$Text::JoinMatch = "Join Match"; $Text::Players = "Players"; $Text::Ready = "Ready"; $Text::ExitGame = "Exit"; @@ -259,6 +260,7 @@ $Text::FGG_Players = "Players"; $Text::Mission = "Level"; $Text::MaxPlayers = "Max Players"; $Text::PrivateSlots = "Private Slots"; +$Text::IsPrivate = "Private Game"; ;------------------------------------------------------------------------------- ; Leaderboard Select Menu @@ -273,6 +275,8 @@ $Text::LobbyHostName = "Host:"; $Text::LobbyHostLevel = "Level:"; $Text::LobbyHostPrivateSlots = "Private Slots"; $Text::LobbyHostPublicSlots = "Public Slots"; +$Text::LobbyHostInviteCode = "Invite Code"; +$Text::LobbyHostInviteVisibility = "Invite Visibility"; ; separators $Text::Colon = ":"; @@ -333,6 +337,7 @@ $Text::FramerateOptions = "Unlimited\tVsync\t30\t60\t75\t120\t200\tCustom"; $Text::MarbleSkin = "Marble Type"; $Text::UIStyle = "UI Style"; $Text::UIStyleOptions = "Mouse/Keyboard\tXbox 360"; +$Text::InviteVisibilityOptions = "Hidden\tVisible"; ;This string is no longer used $Text::MarbleOptions = "one\ttwo\tthree\tfour\tfive\tsix\tseven\teight\tnine\tten\televen\ttwelve\tthirteen\tfourteen\tfifteen\tsixteen\tseventeen\teighteen\tnineteen\ttwenty"; ;$Text::MarbleColor = "Marble Color"; diff --git a/game/common/server/clientConnection.cs b/game/common/server/clientConnection.cs index 82ee88c2..8d737b27 100644 --- a/game/common/server/clientConnection.cs +++ b/game/common/server/clientConnection.cs @@ -51,9 +51,18 @@ function updateServerParams() { echo("Connect request from: " @ %netAddress); - if($Server::PlayerCount >= $pref::Server::MaxPlayers) + %flag = $Server::PlayerCount >= ($pref::Server::MaxPlayers - $Pref::Server::PrivateSlots); + + if (%invited $= "" && %flag) return "CR_SERVERFULL"; - + + if ((%invited !$= "" && %invited != 0) && $Server::PrivatePlayerCount >= $Pref::Server::PrivateSlots && %flag) + return "CR_SERVERFULL"; + + if (%invited $= "" && $Server::IsPrivate) + return "CR_SERVERFULL"; + + %banEntry = $banlist[%xbLiveId]; if (%banEntry !$= "") { @@ -83,6 +92,11 @@ function updateServerParams() return "CR_DEMOREJECT"; } } + + if (%invited !$= "" && %invited !$= $Server::InviteCode) + { + return "CHR_PASSWORD"; + } // kick any players with this xblive id //if (%xbLiveId !$= "") // TODO: uncomment when this is set up in the engine @@ -145,8 +159,6 @@ function updateServerParams() // note the time that this client joined %client.joinTime = getSimTime(); %client.joinInProgress = $Game::State $= "play"; - - $Server::PlayerCount++; // the client was invited and we have a free private slot for them, stick them into it if (%client.invited && $Server::PrivatePlayerCount < $Pref::Server::PrivateSlots) @@ -154,6 +166,10 @@ function updateServerParams() %client.usingPrivateSlot = true; $Server::PrivatePlayerCount++; } + else + { + $Server::PlayerCount++; + } } // Inform all clients of server global parameters - need to do this early, so that the information is available @@ -345,13 +361,15 @@ function isNameUnique(%name) echo("CDROP: " @ %client @ " " @ %client.getAddress()); - $Server::PlayerCount--; + if (%client.usingPrivateSlot) $Server::PrivatePlayerCount--; - + else + $Server::PlayerCount--; + // Reset the server if everyone has left the game - if( $Server::PlayerCount == 0 && $Server::Dedicated) + if ($Server::PlayerCount == 0 && $Server::Dedicated) schedule(0, 0, "resetServerDefaults"); // If everyone has left game, destroy it diff --git a/game/common/server/server.cs b/game/common/server/server.cs index 7fbc53f6..179bb428 100644 --- a/game/common/server/server.cs +++ b/game/common/server/server.cs @@ -11,7 +11,7 @@ function portInit(%port) %failCount = 0; while(%failCount < 10 && !setNetPort(%port)) { echo("Port init failed on port " @ %port @ " trying next port."); - %port++; %failCount++; + %port += 3; %failCount++; } } diff --git a/game/marble/client/defaults.cs b/game/marble/client/defaults.cs index 02769589..dc71ffc5 100644 --- a/game/marble/client/defaults.cs +++ b/game/marble/client/defaults.cs @@ -16,6 +16,7 @@ $pref::HudMessageLogSize = 40; $pref::ChatHudLength = 1; $pref::useStencilShadows = true; +$pref::forceDirectConnect = false; $pref::Input::LinkMouseSensitivity = 1; // DInput keyboard, mouse, and joystick prefs $pref::displayMPHelpText = false; @@ -48,6 +49,7 @@ $pref::OpenGL::forcePalettedTexture = "0"; $pref::OpenGL::maxHardwareLights = 3; $pref::VisibleDistanceMod = 1.0; +$pref::Lobby::InviteVisibility = true; /// The sound provider to select at startup. Typically /// this is DirectSound, OpenAL, or XACT. There is also diff --git a/game/marble/client/init.cs b/game/marble/client/init.cs index f5d97c93..d06db056 100644 --- a/game/marble/client/init.cs +++ b/game/marble/client/init.cs @@ -45,7 +45,7 @@ function initClient() // Make sure this variable reflects the correct state. $Server::Dedicated = false; // Game information used to query the master server - $Client::GameTypeQuery = "Marble Game"; + $Client::GameTypeQuery = "OpenMBU"; $Client::MissionTypeQuery = "Any"; // Default level qualification if (!$pref::QualifiedLevel["Beginner"]) @@ -134,6 +134,7 @@ function initClient() exec("./ui/GameEndGui.gui"); exec("./ui/StartupErrorGui.gui"); exec("./ui/controlerDisplayGui.gui"); + exec("./ui/joinGameGui.gui"); //exec("./ui/AboutGui.gui"); //exec("./ui/LevelScoresGui.gui"); @@ -444,8 +445,63 @@ function waitForPreviewLevel() $Client::GameLoaded = true; } + +// Connect to a server using a chosen %method +// Methods: +// 0: Direct Connect +// 1: Arranged Connect through NAT hole punching +// 2: Relay Connect using a relay server +function connectUsing(%address, %method) +{ + $disconnectGui = RootGui.contentGui; + GameMissionInfo.setMode(GameMissionInfo.MPMode); + + if ($EnableFMS) + { + %missionIndex = GameMissionInfo.getCurrentIndex(); + if (%missionIndex == -1) + %missionIndex = 0; + + GameMissionInfo.selectMission(%missionIndex); + } + RootGui.setContent(MissionLoadingGui); + + echo("ESTABLISH CONNECTION" SPC %address SPC %mp SPC %invited); + if (isObject(ServerConnection)) + ServerConnection.delete(); + + // clear the scores gui here so that our the client id from the preview server doesn't + // show up in the scores list. + PlayerListGui.clear(); + // reset client Id since we are connecting to a different server + $Player::ClientId = 0; + + %conn = new GameConnection(ServerConnection); + RootGroup.add(ServerConnection); + + %xbLiveVoice = 0; // XBLiveGetVoiceStatus(); + + // we expect $Player:: variables to be properly populated at this point + %isDemoLaunch = isDemoLaunch(); + %conn.setConnectArgs($Player::Name, $Player::XBLiveId, %xbLiveVoice, %invited, %isDemoLaunch); + %conn.setJoinPassword($Client::Password); + + $Client::connectedMultiplayer = true; + $Game::SPGemHunt = false; + + if (%method == 0) { + %conn.connect(%address); + } else if (%method == 1) { + %conn.arrangeConnection(%address); + } else if (%method == 2) { + %conn.relayConnection(%address); + } + + clearClientGracePeroid(); +} + // Manually connect to server by ip -function connectManual(%address, %invited) +function connectManual(%address, %local, %invited) { $disconnectGui = RootGui.contentGui; @@ -463,7 +519,7 @@ function connectManual(%address, %invited) RootGui.setContent(MissionLoadingGui); if ($EnableFMS) - establishConnection(%address, true, %invited); + establishConnection(%address, true, %local, %invited); else connectToServer(%address, %invited); } @@ -471,6 +527,7 @@ function connectManual(%address, %invited) // connect to a server. if address is empty a local connect is assumed function connectToServer(%address,%invited) { + echo("CONNECT TO SERVER" SPC %address SPC %invited); if (isObject(ServerConnection)) ServerConnection.delete(); @@ -509,7 +566,10 @@ function connectToServer(%address,%invited) } $Client::connectedMultiplayer = true; $Game::SPGemHunt = false; - %conn.connect(%address); + if ($pref::forceDirectConnect) + %conn.connect(%address); + else + %conn.arrangeConnection(%address); } clearClientGracePeroid(); @@ -544,8 +604,9 @@ function connectToPreviewServer() } // connect to a server. if address is empty a local connect is assumed -function establishConnection(%address, %mp, %invited) +function establishConnection(%address, %mp, %isLocal, %invited) { + echo("ESTABLISH CONNECTION" SPC %address SPC %mp SPC %invited); if (isObject(ServerConnection)) ServerConnection.delete(); @@ -588,7 +649,10 @@ function establishConnection(%address, %mp, %invited) } $Client::connectedMultiplayer = true; $Game::SPGemHunt = false; - %conn.connect(%address); + if (%isLocal || $pref::forceDirectConnect) + %conn.connect(%address); + else + %conn.arrangeConnection(%address); } clearClientGracePeroid(); diff --git a/game/marble/client/scripts/default.bind.cs b/game/marble/client/scripts/default.bind.cs index 6b692b59..b27dd715 100644 --- a/game/marble/client/scripts/default.bind.cs +++ b/game/marble/client/scripts/default.bind.cs @@ -114,8 +114,10 @@ function pauseToggle(%defaultItem) enterPreviewMode(); } // client's just disconnect - else if ($Client::connectedMultiplayer) // this is also true for hosts so we check hosting first + else if ($Client::connectedMultiplayer) { // this is also true for hosts so we check hosting first disconnect(); + RootGui.setContent(MultiPlayerGui); + } // if play gui is awake, return to Level Preview. Otherwise Quit else if (PlayGui.isAwake() || MissionLoadingGui.isAwake()) { diff --git a/game/marble/client/scripts/serverConnection.cs b/game/marble/client/scripts/serverConnection.cs index f74df047..99a9340d 100644 --- a/game/marble/client/scripts/serverConnection.cs +++ b/game/marble/client/scripts/serverConnection.cs @@ -385,3 +385,21 @@ function enterPreviewMode(%clientDropCode) RootGui.setContent($disconnectGui); } } + +function GameConnection::onConnectStatus(%this, %statusCode) +{ + if (RootGui.contentGui == MissionLoadingGui && !$Server::Hosting) + { + switch (%statusCode) + { + case 1: + RootGui.setCenterText("Punching hole in NAT."); + case 2: + RootGui.setCenterText("Hole punching successful, trying to connect."); + case 3: + RootGui.setCenterText("Connecting.."); + case 4: + RootGui.setCenterText("Trying relay."); + } + } +} \ No newline at end of file diff --git a/game/marble/client/ui/GamePauseGui.gui b/game/marble/client/ui/GamePauseGui.gui index 1b6deb5f..dc839a83 100644 --- a/game/marble/client/ui/GamePauseGui.gui +++ b/game/marble/client/ui/GamePauseGui.gui @@ -70,6 +70,10 @@ function GamePauseGui::onWake(%this) LevelTitle.setText(%level); RootGui.setTitle($Text::Paused); + + if ($Server::Hosting && $pref::Lobby::InviteVisibility) + LevelTitle.setText($Text::LobbyHostInviteCode @ $Text::Colon SPC $Server::InviteCode); + if ($pref::UI::LegacyUI) { RootGui.setA($Text::Go); diff --git a/game/marble/client/ui/RootGui.gui b/game/marble/client/ui/RootGui.gui index 5126f054..f2df9d9a 100644 --- a/game/marble/client/ui/RootGui.gui +++ b/game/marble/client/ui/RootGui.gui @@ -417,7 +417,7 @@ new GuiControl(RootCenterCtrl) { profile = "GuiDefaultProfile"; horizSizing = "right"; vertSizing = "top"; - position = isWidescreen()? "262 50" : "102 34"; + position = isWidescreen()? "235 5" : "95 4"; extent = "500 60"; minExtent = "8 2"; bitmap = "./xbox/roundedBG"; diff --git a/game/marble/client/ui/createGameGui.gui b/game/marble/client/ui/createGameGui.gui index 673f6b4d..0fdd963e 100644 --- a/game/marble/client/ui/createGameGui.gui +++ b/game/marble/client/ui/createGameGui.gui @@ -9,8 +9,8 @@ new GuiControl(CreateGameGui) { new GuiXboxOptionListCtrl(CreateGameOptionList) { profile = isWidescreen()? "TextOptionListProfile" : "TextOptionListSmallWideProfile"; - position = isWidescreen()? "360 373" : "-20 195"; - extent = isWidescreen()? "835 400" : "660 250"; + position = isWidescreen()? "360 323" : "-20 145"; + extent = isWidescreen()? "835 430" : "660 280"; horizSizing = isWidescreen()? "right" : "left"; vertSizing = isWidescreen()? "bottom" : "top"; // there can only be two columns; these values are percentages of total extent @@ -74,11 +74,14 @@ function CreateGameGui::show(%this,%backGui) %this.missionIndex = 0; %this.maxPlayersIndex = 1; %this.privateSlotsIndex = 2; + %this.privateGameIndex = 3; CreateGameOptionList.addRow($Text::Mission, GameMissionInfo.getMissionDisplayNameList(), 18); CreateGameOptionList.addRow($Text::MaxPlayers, GetOptionListRangeText(%this.maxPlayersMin, $Server::AbsMaxPlayers), 8); if( allowPrivateSlots() ) CreateGameOptionList.addRow($Text::PrivateSlots, $Text::None TAB GetOptionListRangeText(1, $Server::AbsMaxPlayers), 8); + + CreateGameOptionList.addRow($Text::IsPrivate, $Text::No TAB $Text::Yes, 8); //CreateGameOptionList.addRow($Text::GM23, $Text::S40 TAB GetOptionListRangeText(1, $Server::AbsMaxBots)); //CreateGameOptionList.addRow($Text::GM29, GetOptionListRangeText(5, 15, 5)); @@ -291,6 +294,17 @@ function CreateGameGui::onGameCreated(%this) RootGui.setContent(LobbyGui); } +function generateInviteCode() +{ + %availableChars = "0123456789"; + %code = ""; + for (%i = 0; %i < 6; %i++) + { + %code = %code @ getSubStr(%availableChars, getRandom(0, 9), 1); + } + return %code; +} + function CreateGameGui::createGame(%this) { %this.creatingGame = true; @@ -310,7 +324,10 @@ function CreateGameGui::createGame(%this) $pref::Server::missionId = $Server::MissionId; $pref::Server::MaxPlayers = CreateGameOptionList.getOptionIndex(%this.maxPlayersIndex) + CreateGameGui.maxPlayersMin; + $Server::IsPrivate = CreateGameOptionList.getOptionIndex(%this.privateGameIndex); + $Server::InviteCode = generateInviteCode(); + if( allowPrivateSlots() ) $pref::Server::PrivateSlots = CreateGameOptionList.getOptionIndex(%this.privateSlotsIndex); else @@ -323,7 +340,7 @@ function CreateGameGui::createGame(%this) //$Game::Duration = $pref::Server::TimeLimit * 60; //$Server::StayMission = true; $Server::ReturnToLobby = true; - $pref::Server::Name = XBLiveGetUserName(); + $pref::Server::Name = $Player::Name @ "'s Server"; // XBLiveGetUserName(); //MissionSequence.buildMissionSequence(); //MissionSequence.setCurrentMission($Server::PostLobbyMissionFile); diff --git a/game/marble/client/ui/defaultGameProfiles.cs b/game/marble/client/ui/defaultGameProfiles.cs index e51b8af0..fbe433f3 100644 --- a/game/marble/client/ui/defaultGameProfiles.cs +++ b/game/marble/client/ui/defaultGameProfiles.cs @@ -182,6 +182,27 @@ hitArea = "20 76"; }; +new GuiControlProfile(TextEditProfile) +{ + fontType = "Arial Bold"; + opaque = true; + fillColor = "0 0 0 0"; + fillColorHL = "0 0 0 0"; + border = false; + borderThickness = 2; + borderColor = "0 0 0"; + fontColor = $XBOX::MenuTextColor; + fontColorHL = $XBOX::MenuTextColorSelected; + fontColorNA = $XBOX::MenuTextColorDisabled; + textOffset = "0 2"; + autoSizeWidth = false; + autoSizeHeight = true; + tab = true; + canKeyFocus = true; + + fontSize = 24; +}; + new GuiControlProfile(TextMenuListSmallProfile : TextMenuListProfile) { fontSize = 24; diff --git a/game/marble/client/ui/findGameGui.gui b/game/marble/client/ui/findGameGui.gui index 1da82cfd..c89c693f 100644 --- a/game/marble/client/ui/findGameGui.gui +++ b/game/marble/client/ui/findGameGui.gui @@ -122,6 +122,11 @@ function onServerQueryStatus(%status, %msg, %value) { if (%status !$= "done" && %status !$= "error") return; + + if (%value == 0) // No master servers found + $queryOffline = true; + else + $queryOffline = false; eval($pcsearchcallback); } @@ -159,15 +164,10 @@ function PC_XBLiveSearchForMatches(%gamemodefilter, %missionfilter, %maxplayersf %mis = %missionfilter; if (%mis $= "-1") %mis = "any"; + + %filterFlags |= 1 << 7; // Current version only - if (FindGameGui.useMaster) - { - queryMasterServer(0, "any", %mis, 0, 100, 100, 2, 1000, 0, %filterFlags); - } - else - { - queryLanServers(1000, 0, "any", %mis, 0, 100, 100, 2, 1000, 0, %filterFlags, 1, $pcsearchusefilters); - } + queryMasterServer(28000, 0, "OpenMBU", %mis, 0, %maxplayersfilter, 100, 2, 1000, 0, %filterFlags); } // ---------------------------------------------------------------------------- @@ -440,16 +440,7 @@ function FindGameGui::onSearchComplete(%this,%type) if (isPCBuild() || !$Client::UseXBLiveMatchMaking) { %sc = getServerCount(); - if (%sc == 0 && %this.useMaster) - { - %this.useMaster = false; - - %this.pcSearchActive = true; - PC_XBLiveSearchForMatches(%this.gamemodefilter, %this.missionfilter, - %this.maxplayersfilter, -1, "FindGameGui.onSearchComplete(pc);"); - return; - } - echo(%sc @ " games returned from LAN search."); + echo(%sc @ " games returned from search."); for (%i = 0; %i < %sc; %i++) { // no filtering in PC build, we just add everything @@ -461,7 +452,10 @@ function FindGameGui::onSearchComplete(%this,%type) // for testing //%ping = 120; %gameIcon = ""; - %rowText = %playerStr TAB %gameIcon TAB %ping TAB "1" TAB %i TAB "\c1" @ "LAN:" SPC $ServerInfo::Name; // 1 == is pc result + if ($ServerInfo::IsLocal) + %rowText = %playerStr TAB %gameIcon TAB %ping TAB "1" TAB %i TAB "\c1" @ "LAN:" SPC $ServerInfo::Name; // 1 == is pc result + else + %rowText = %playerStr TAB %gameIcon TAB %ping TAB "1" TAB %i TAB "\c1" @ $ServerInfo::Name; // 1 == is pc result FindGameServerList.addRow(%count, %rowText); %this.updatePingIcon(%count); %count++; @@ -730,9 +724,12 @@ function FindGameGui::joinGame(%this, %index, %invited) %this.joiningGame = true; RootGui.setContent(MissionLoadingGui); - if ($EnableFMS) - establishConnection($ServerInfo::Address, true); - else + if ($EnableFMS) { + if ($ServerInfo::IsLocal) + establishConnection($ServerInfo::Address, true, true); + else + establishConnection($ServerInfo::Address, true); + } else connectToServer($ServerInfo::Address); return; } @@ -796,7 +793,7 @@ function FindGameGui::onConnectComplete(%this) RootGui.setContent(MissionLoadingGui); if ($EnableFMS) - establishConnection($XBLive::secureHostAddress, true, $Client::Invited); + establishConnection($XBLive::secureHostAddress, true, false, $Client::Invited); else connectToServer($XBLive::secureHostAddress, $Client::Invited); } diff --git a/game/marble/client/ui/joinGameGui.gui b/game/marble/client/ui/joinGameGui.gui new file mode 100644 index 00000000..129a90b5 --- /dev/null +++ b/game/marble/client/ui/joinGameGui.gui @@ -0,0 +1,348 @@ +//--- OBJECT WRITE BEGIN --- +new GuiControl(JoinGameGui) { + profile = "GuiDefaultProfile"; + horizSizing = "width"; + vertSizing = "height"; + extent = "640 480"; + minExtent = "8 8"; + position = "0 0"; + + new GuiXboxOptionListCtrl(InviteCodeMenuCtrl) { + profile = isWidescreen()? "TextOptionListProfile" : "TextOptionListSmallWideProfile"; + position = isWidescreen()? "360 211" : "-30 100"; + extent = isWidescreen()? "835 400" : "672 250"; + horizSizing = isWidescreen()? "right" : "left"; + vertSizing = "center"; + // there can only be two columns; these values are percentages of total extent + columns = isWidescreen()? "40 60" : "49 51"; + // each column can have a left and right margin, specified here. order is + // C1L C1R C2L C2R. amount is in pixels + columnMargins = isWidescreen()? "0 0 3 35" : "0 0 2 25"; + // for debugging, show the region update rect and column rects (with margins) + showRects = 0; + + // data is dynamically added to this option list in the show() function + + new GuiTextCtrl(InviteCodeInput) { + Profile = "TextEditProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "640 33"; + Extent = "235 18"; + MinExtent = "8 2"; + Visible = "1"; + text = ""; + }; + + new GuiControl(NumpadCtrl) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + extent = "800 300"; + minExtent = "8 8"; + position = "0 60"; + + new GuiXboxButtonCtrl(Numpad1) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "240 150"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "1"; + command = "JoinGameGui.textInput(1);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad2) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "320 150"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "2"; + command = "JoinGameGui.textInput(2);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad3) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "400 150"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "3"; + command = "JoinGameGui.textInput(3);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad4) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "240 80"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "4"; + command = "JoinGameGui.textInput(4);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad5) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "320 80"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "5"; + command = "JoinGameGui.textInput(5);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad6) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "400 80"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "6"; + command = "JoinGameGui.textInput(6);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad7) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "240 10"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "7"; + command = "JoinGameGui.textInput(7);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad8) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "320 10"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "8"; + command = "JoinGameGui.textInput(8);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad9) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "400 10"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "9"; + command = "JoinGameGui.textInput(9);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(Numpad0) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "240 220"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "0"; + command = "JoinGameGui.textInput(0);"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(NumpadOK) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "320 220"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "Ok"; + command = "JoinGameGui.joinGame();"; + visible = "1"; + }; + + new GuiXboxButtonCtrl(NumpadDel) { + profile = "TextMenuButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "400 220"; + extent = "120 94"; + minExtent = "8 2"; + bitmap = ""; + bitmapAlign = "right"; + text = "Delete"; + command = "JoinGameGui.textInput(D);"; + visible = "1"; + }; + }; + }; +}; + +function JoinGameGui::show(%this) +{ + RootGui.setTitle($Text::JoinGame); + RootGui.setB($Text::Back); + + if (GameMissionInfo.getMode() !$= GameMissionInfo.MPMode) + { + GameMissionInfo.setMode(GameMissionInfo.MPMode); + GameMissionInfo.setDefaultMission(); + } + + JoinGameGui.inviteCode = ""; + InviteCodeInput.setText(JoinGameGui.inviteCode); + + InviteCodeMenuCtrl.clear(); + InviteCodeMenuCtrl.addRow("Invite Code", "", 8); + InviteCodeMenuCtrl.setButtonsEnabled(false); + + $NumpadGrid[0, -1] = Numpad0; + $NumpadGrid[0, 0] = Numpad1; + $NumpadGrid[1, 0] = Numpad2; + $NumpadGrid[2, 0] = Numpad3; + $NumpadGrid[0, 1] = Numpad4; + $NumpadGrid[1, 1] = Numpad5; + $NumpadGrid[2, 1] = Numpad6; + $NumpadGrid[0, 2] = Numpad7; + $NumpadGrid[1, 2] = Numpad8; + $NumpadGrid[2, 2] = Numpad9; + $NumpadGrid[2, -1] = NumpadDel; + $NumpadGrid[1, -1] = NumpadOk; + + $NumpadCoordX = 0; + $NumpadCoordY = 0; + + $NumpadCtrl = Numpad1; +} + +function JoinGameGui::onA(%this) +{ + sfxPlay( AudioButtonDown ); + eval($NumpadCtrl.command); + return true; +} + +function JoinGameGui::onB(%this) +{ + sfxPlay( AudioButtonDown ); + %this.exit(); + return true; +} + +function JoinGameGui::exit(%this) +{ + RootGui.setContent(MultiPlayerGui); + cancel(JoinGameGui.joinTimeout); +} + +function JoinGameGui::joinGame(%this) +{ + echo("Joining game with invite code " @ JoinGameGui.inviteCode); + joinGameByInvite(JoinGameGui.inviteCode, "joinGameFound", "joinGameNotFound"); + cancel(JoinGameGui.joinTimeout); + JoinGameGui.joinTimeout = schedule(5000, 0, "joinGameNotFound"); + RootGui.setContent(MissionLoadingGui); +} + +function joinGameFound(%ip, %isLocal) +{ + cancel(JoinGameGui.joinTimeout); + setNetPort(0); // Get random port pls + establishConnection(%ip, true, %isLocal, JoinGameGui.inviteCode); +} + +function joinGameNotFound() +{ + cancel(JoinGameGui.joinTimeout); + echo("Not Found"); + if (RootGui.contentGui == MissionLoadingGui) + RootGui.setContent(JoinGameGui); +} + +function JoinGameGui::textInput(%this, %chr) +{ + cancel(JoinGameGui.joinTimeout); + if (%chr !$= "D") + { + if (strlen(JoinGameGui.inviteCode) < 6) + JoinGameGui.inviteCode = JoinGameGui.inviteCode @ %chr; + } + else + { + if (strlen(JoinGameGui.inviteCode) > 0) + JoinGameGui.inviteCode = getSubStr(JoinGameGui.inviteCode, 0, strlen(JoinGameGui.inviteCode) - 1); + } + InviteCodeInput.setText(JoinGameGui.inviteCode); +} + +function JoinGameGui::moveDir(%this, %x, %y) +{ + $NumpadCoordX += %x; + $NumpadCoordY += %y; + if ($NumpadCoordX > 2) $NumpadCoordX = 0; + if ($NumpadCoordX < 0) $NumpadCoordX = 2; + if ($NumpadCoordY > 2) $NumpadCoordY = -1; + if ($NumpadCoordY < -1) $NumpadCoordY = 2; + $NumpadCtrl.setHover(false); + $NumpadCtrl = $NumpadGrid[$NumpadCoordX, $NumpadCoordY]; + $NumpadCtrl.setHover(true); +} + +function InviteCodeInput::onLeft(%this, %pressed) +{ + JoinGameGui.moveDir(-1, 0); + return true; +} + +function InviteCodeInput::onRight(%this, %pressed) +{ + JoinGameGui.moveDir(1, 0); + return true; +} + +function InviteCodeInput::onUp(%this, %pressed) +{ + JoinGameGui.moveDir(0, 1); + return true; +} + +function InviteCodeInput::onDown(%this, %pressed) +{ + JoinGameGui.moveDir(0, -1); + return true; +} \ No newline at end of file diff --git a/game/marble/client/ui/lobbyGui.gui b/game/marble/client/ui/lobbyGui.gui index d7924594..00c5fb15 100644 --- a/game/marble/client/ui/lobbyGui.gui +++ b/game/marble/client/ui/lobbyGui.gui @@ -293,8 +293,10 @@ function LobbyGui::updateHostingStatus(%this) } //} - if (%this.hosting && !isDemoLaunch() && !XBLiveIsRanked()) + if (%this.hosting && !isDemoLaunch() && !XBLiveIsRanked()) { RootGui.setLS($Text::ChangeLevels); + RootGui.setX($Text::LobbyHostInviteVisibility); + } } } @@ -698,14 +700,21 @@ function LobbyGui::setHostInfo(%this, %hostName, %gameMode, %mapname, %priSlotsU //%gamemodetag = (isEnglish()) ? $text::LM7 @ " " : ""; // hide " %gamemodetag = $Text::LobbyHostName; - LobbyGuiHostGameInfo.setText( - "" @ %gamemodetag SPC %hostName @ "\n" @ + + LobbyGuiHostGameInfo.beforeText = "" @ %gamemodetag SPC %hostName @ "\n" @ $Text::LobbyHostLevel SPC %missionName @ "\n" @ "" @ $Text::LobbyHostPrivateSlots @ $Text::Colon SPC %priSlotsUsed @ $Text::Slash @ %priSlotsFree + %priSlotsUsed @ $Text::Comma SPC $Text::LobbyHostPublicSlots @ $Text::Colon SPC - %pubSlotsUsed @ $Text::Slash @ %pubSlotsFree + %pubSlotsUsed @ " "); - + %pubSlotsUsed @ $Text::Slash @ %pubSlotsFree + %pubSlotsUsed; + + if ($pref::Lobby::InviteVisibility) + LobbyGuiHostGameInfo.setText(LobbyGuiHostGameInfo.beforeText @ + ($Server::Hosting ? ($Text::Comma SPC $Text::LobbyHostInviteCode @ $Text::Colon SPC + $Server::InviteCode @ " ") : "")); + else + LobbyGuiHostGameInfo.setText(LobbyGuiHostGameInfo.beforeText); + LobbyGuiSelection.clear(); LobbyGuiSelection.setButtonsEnabled(%this.hosting); LobbyGuiSelection.addRow($Text::Level, %mapname, 18); @@ -956,6 +965,18 @@ function LobbyGuiPlayerList::onSelect( %this, %id, %text ) RootGui.setY( $Text::SelectPlayer ); } +function LobbyGui::onX(%this) +{ + $pref::Lobby::InviteVisibility = !$pref::Lobby::InviteVisibility; + + if ($pref::Lobby::InviteVisibility) + LobbyGuiHostGameInfo.setText(LobbyGuiHostGameInfo.beforeText @ + ($Server::Hosting ? ($Text::Comma SPC $Text::LobbyHostInviteCode @ $Text::Colon SPC + $Server::InviteCode @ " ") : "")); + else + LobbyGuiHostGameInfo.setText(LobbyGuiHostGameInfo.beforeText); +} + function LobbyGui::onY(%this) { if (%this.isLoading()) diff --git a/game/marble/client/ui/miscOptionsGui.gui b/game/marble/client/ui/miscOptionsGui.gui index 5d718f0c..5bf7591b 100644 --- a/game/marble/client/ui/miscOptionsGui.gui +++ b/game/marble/client/ui/miscOptionsGui.gui @@ -69,6 +69,11 @@ function miscOptionsGui::show(%this, %backGui) miscOptionsList.setOptionIndex(1, %uiStyleIndex); + // Invite visibility + + miscOptionsList.addRow($Text::LobbyHostInviteCode, $Text::InviteVisibilityOptions, 8); + miscOptionsList.setOptionIndex(2, $pref::Lobby::InviteVisibility); + RootGui.setA( $Text::OK ); RootGui.setTitle( strupr($Text::HOMiscOptions) ); } @@ -90,6 +95,9 @@ function miscOptionsList::onOptionChange(%this, %increase) // ui style $pref::UI::LegacyUI = %val; echo("UIStyle = " @ $pref::UI::LegacyUI); + case 2: + $pref::Lobby::InviteVisibility = %val; + echo("InviteVisibility = " @ $pref::Lobby::InviteVisibility); } } diff --git a/game/marble/client/ui/multiPlayerGui.gui b/game/marble/client/ui/multiPlayerGui.gui index 4abf198d..b76230b2 100644 --- a/game/marble/client/ui/multiPlayerGui.gui +++ b/game/marble/client/ui/multiPlayerGui.gui @@ -49,10 +49,13 @@ function MultiPlayerGui::onA(%this) } RootGui.setContent(CreateGameGui); + case 3: + RootGui.setContent(JoinGameGui); + case 4: // Auto enter the MP leaderboards RootGui.setContent( LevelScoresGui, GameMissionInfo.MPMode, $Leaderboard::MPScrumOverall, %this ); - case 4: + case 5: // Show marketplace UI, this is kinda hacked since it's not a demo launch clientShowMarketplaceUI(); $PDLCMarketView = true; @@ -133,6 +136,7 @@ function MultiPlayerGui::show(%this) MultiPlayerMenu.addRow($Text::QuickMatch, "", 0, 2); MultiPlayerMenu.addRow($Text::OptiMatch, "", 0, 2); MultiPlayerMenu.addRow($Text::CreateMatch, "", 0, 2); + MultiPlayerMenu.addRow($Text::JoinMatch, "", 0, 2); MultiPlayerMenu.addRow($Text::LeaderBoards, "", 0, 6); RootGui.setTitle($Text::MultiplayerMenu); diff --git a/game/marble/server/defaults.cs b/game/marble/server/defaults.cs index 1a457f87..398fdd21 100644 --- a/game/marble/server/defaults.cs +++ b/game/marble/server/defaults.cs @@ -26,7 +26,7 @@ // The network port is also defined by the client, this value // overrides pref::net::port for dedicated servers -$Pref::Server::Port = 1000; +$Pref::Server::Port = 28000; // If the password is set, clients must provide it in order // to connect to the server diff --git a/game/marble/server/scripts/game.cs b/game/marble/server/scripts/game.cs index 04fdf2da..e2fbbfed 100644 --- a/game/marble/server/scripts/game.cs +++ b/game/marble/server/scripts/game.cs @@ -91,7 +91,7 @@ function onServerCreated() { // Server::GameType is sent to the master server. // This variable should uniquely identify your game and/or mod. - $Server::GameType = "Marble Game"; + $Server::GameType = "OpenMBU"; // Server::MissionType sent to the master server. Clients can // filter servers based on mission type. diff --git a/game/marble/server/scripts/gameCommands.cs b/game/marble/server/scripts/gameCommands.cs index 340101dc..5e359042 100644 --- a/game/marble/server/scripts/gameCommands.cs +++ b/game/marble/server/scripts/gameCommands.cs @@ -29,7 +29,7 @@ function serverAreAllPlayersReady() function serverGetPublicSlotsUsed() { - return $Server::PlayerCount - $Server::PrivatePlayerCount; + return $Server::PlayerCount; } function serverGetPrivateSlotsUsed()