Files
SLikeNet/DependentExtensions/Lobby2/Lobby2Server.cpp
2025-11-24 14:19:51 +05:30

736 lines
22 KiB
C++

/*
* Original work: Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
*
*
* Modified work: Copyright (c) 2017, SLikeSoft UG (haftungsbeschränkt)
*
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
* license found in the license.txt file in the root directory of this source tree.
*/
#include "Lobby2Server.h"
#include "slikenet/assert.h"
#include "slikenet/MessageIdentifiers.h"
//#define __INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN
#ifdef __INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN
#include "RoomsPlugin.h"
#endif
using namespace SLNet;
int Lobby2Server::UserCompByUsername( const RakString &key, Lobby2Server::User * const &data )
{
if (key < data->userName)
return -1;
if (key==data->userName)
return 0;
return 1;
}
Lobby2Server::Lobby2Server()
{
DataStructures::OrderedList<SystemAddress, SystemAddress>::IMPLEMENT_DEFAULT_COMPARISON();
DataStructures::OrderedList<RakString, RakString>::IMPLEMENT_DEFAULT_COMPARISON();
roomsPlugin=0;
roomsPluginAddress=UNASSIGNED_SYSTEM_ADDRESS;
}
Lobby2Server::~Lobby2Server()
{
Clear();
}
void Lobby2Server::SendMsg(Lobby2Message *msg, const DataStructures::List<SystemAddress> &recipients)
{
SLNet::BitStream bs;
bs.Write((MessageID)ID_LOBBY2_SEND_MESSAGE);
bs.Write((MessageID)msg->GetID());
msg->Serialize(true,true,&bs);
SendUnifiedToMultiple(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, recipients);
}
void Lobby2Server::Update(void)
{
while (threadActionQueue.Size())
{
threadActionQueueMutex.Lock();
if (threadActionQueue.Size())
{
ThreadAction ta = threadActionQueue.Pop();
threadActionQueueMutex.Unlock();
if (ta.action==L2MID_Client_Logoff)
{
OnLogoff(&ta.command, false);
}
else if (ta.action==L2MID_Client_Login)
{
OnLogin(&ta.command, false);
}
else if (ta.action==L2MID_Client_ChangeHandle)
{
OnChangeHandle(&ta.command, false);
}
}
else
{
threadActionQueueMutex.Unlock();
break;
}
}
if (threadPool.HasOutputFast() && threadPool.HasOutput())
{
Lobby2ServerCommand c = threadPool.GetOutput();
c.lobby2Message->ServerPostDBMemoryImpl(this, c.callingUserName);
if (c.returnToSender)
{
for (unsigned long i=0; i < callbacks.Size(); i++)
{
if (c.lobby2Message->callbackId==(uint32_t)-1 || c.lobby2Message->callbackId==callbacks[i]->callbackId)
c.lobby2Message->CallCallback(callbacks[i]);
}
SLNet::BitStream bs;
bs.Write((MessageID)ID_LOBBY2_SEND_MESSAGE);
bs.Write((MessageID)c.lobby2Message->GetID());
c.lobby2Message->Serialize(true,true,&bs);
// Have the ID to send to, but not the address. The ID came from the thread, such as notifying another user
if (c.callerSystemAddresses.Size()==0)
{
unsigned int i;
if (c.callerUserId!=0)
{
for (i=0; i < users.Size(); i++)
{
if (users[i]->callerUserId==c.callerUserId)
{
c.callerSystemAddresses=users[i]->systemAddresses;
c.callerGuids=users[i]->guids;
/*
if (c.requiredConnectionAddress!=UNASSIGNED_SYSTEM_ADDRESS)
{
// This message refers to another user that has to be logged on for it to be sent
bool objectExists;
unsigned int idx;
idx = users.GetIndexFromKey(c.callerSystemAddress,&objectExists);
if (objectExists==false)
{
if (c.deallocMsgWhenDone)
SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
return;
}
}
*/
break;
}
}
}
if (c.callerSystemAddresses.Size()==0 && c.callingUserName.IsEmpty()==false)
{
for (i=0; i < users.Size(); i++)
{
if (users[i]->callerUserId==c.callerUserId)
{
c.callerSystemAddresses=users[i]->systemAddresses;
c.callerGuids=users[i]->guids;
break;
}
}
}
}
else
{
bool objectExists;
unsigned int idx;
idx = users.GetIndexFromKey(c.callingUserName,&objectExists);
if (objectExists &&
c.callingUserName.IsEmpty()==false &&
users[idx]->userName!=c.callingUserName)
{
// Different user, same IP address. Abort the send.
if (c.deallocMsgWhenDone)
SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
return;
}
}
SendUnifiedToMultiple(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, c.callerSystemAddresses);
}
if (c.deallocMsgWhenDone)
SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
}
}
PluginReceiveResult Lobby2Server::OnReceive(Packet *packet)
{
RakAssert(packet);
switch (packet->data[0])
{
case ID_LOBBY2_SEND_MESSAGE:
OnMessage(packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
return RR_CONTINUE_PROCESSING;
}
void Lobby2Server::OnShutdown(void)
{
Clear();
}
void Lobby2Server::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
{
(void)rakNetGUID;
(void)lostConnectionReason;
unsigned int index = GetUserIndexBySystemAddress(systemAddress);
// If systemAddress is a user, then notify his friends about him logging off
if (index!=-1)
{
bool found=false;
User *user = users[index];
for (unsigned int i=0; i < user->systemAddresses.Size(); i++)
{
if (user->systemAddresses[i]==systemAddress)
{
found=true;
user->systemAddresses.RemoveAtIndexFast(i);
break;
}
}
if (found && user->systemAddresses.Size()==0)
{
// Log this logoff due to closed connection
Lobby2Message *lobby2Message = msgFactory->Alloc(L2MID_Client_Logoff);
Lobby2ServerCommand command;
command.lobby2Message=lobby2Message;
command.deallocMsgWhenDone=true;
command.returnToSender=true;
command.callerUserId=users[index]->callerUserId;
command.server=this;
ExecuteCommand(&command);
RemoveUser(index);
}
}
}
void Lobby2Server::OnMessage(Packet *packet)
{
SLNet::BitStream bs(packet->data,packet->length,false);
bs.IgnoreBytes(1); // ID_LOBBY2_SEND_MESSAGE
MessageID msgId;
bs.Read(msgId);
Lobby2MessageID lobby2MessageID = (Lobby2MessageID) msgId;
unsigned int index;
Lobby2Message *lobby2Message = msgFactory->Alloc(lobby2MessageID);
if (lobby2Message)
{
lobby2Message->Serialize(false,false,&bs);
Lobby2ServerCommand command;
command.lobby2Message=lobby2Message;
command.deallocMsgWhenDone=true;
command.returnToSender=true;
index=GetUserIndexBySystemAddress(packet->systemAddress);
if (index!=-1)
{
command.callingUserName=users[index]->userName;
command.callerUserId=users[index]->callerUserId;
}
else
{
if (lobby2Message->RequiresLogin())
{
SLNet::BitStream bs2;
bs2.Write((MessageID)ID_LOBBY2_SEND_MESSAGE);
bs2.Write((MessageID)lobby2Message->GetID());
lobby2Message->resultCode=L2RC_NOT_LOGGED_IN;
lobby2Message->Serialize(true,true,&bs2);
SendUnified(&bs2,packetPriority, RELIABLE_ORDERED, orderingChannel, packet->systemAddress, false);
return;
}
command.callerUserId=0;
}
command.callerSystemAddresses.Push(packet->systemAddress,__FILE__,__LINE__);
command.callerGuids.Push(packet->guid,__FILE__,__LINE__);
command.server=this;
ExecuteCommand(&command);
}
else
{
SLNet::BitStream out;
out.Write((MessageID)ID_LOBBY2_SERVER_ERROR);
out.Write((unsigned char) L2SE_UNKNOWN_MESSAGE_ID);
out.Write((unsigned int) msgId);
SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, packet->systemAddress, false);
}
}
void Lobby2Server::Clear(void)
{
ClearAdminAddresses();
ClearRankingAddresses();
ClearUsers();
ClearConnections();
threadPool.StopThreads();
RakAssert(threadPool.NumThreadsWorking()==0);
unsigned i;
Lobby2ServerCommand c;
for (i=0; i < threadPool.InputSize(); i++)
{
c = threadPool.GetInputAtIndex(i);
if (c.deallocMsgWhenDone && c.lobby2Message)
SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
}
threadPool.ClearInput();
for (i=0; i < threadPool.OutputSize(); i++)
{
c = threadPool.GetOutputAtIndex(i);
if (c.deallocMsgWhenDone && c.lobby2Message)
SLNet::OP_DELETE(c.lobby2Message, __FILE__, __LINE__);
}
threadPool.ClearOutput();
threadActionQueueMutex.Lock();
threadActionQueue.Clear(__FILE__, __LINE__);
threadActionQueueMutex.Unlock();
}
void Lobby2Server::AddAdminAddress(SystemAddress addr)
{
adminAddresses.Insert(addr,addr,false, __FILE__, __LINE__ );
}
bool Lobby2Server::HasAdminAddress(const DataStructures::List<SystemAddress> &addresses)
{
if (addresses.Size()==0)
return true;
unsigned int j;
for (j=0; j < addresses.Size(); j++)
{
if (adminAddresses.HasData(addresses[j]))
return true;
}
return false;
}
void Lobby2Server::RemoveAdminAddress(SystemAddress addr)
{
adminAddresses.RemoveIfExists(addr);
}
void Lobby2Server::ClearAdminAddresses(void)
{
adminAddresses.Clear(false, __FILE__, __LINE__);
}
void Lobby2Server::AddRankingAddress(SystemAddress addr)
{
rankingAddresses.Insert(addr,addr,false, __FILE__, __LINE__ );
}
bool Lobby2Server::HasRankingAddress(const DataStructures::List<SystemAddress> &addresses)
{
if (addresses.Size()==0)
return true;
unsigned int j;
for (j=0; j < addresses.Size(); j++)
{
if (rankingAddresses.HasData(addresses[j]))
return true;
}
return false;
}
void Lobby2Server::RemoveRankingAddress(SystemAddress addr)
{
rankingAddresses.RemoveIfExists(addr);
}
void Lobby2Server::ClearRankingAddresses(void)
{
rankingAddresses.Clear(false, __FILE__, __LINE__);
}
void Lobby2Server::ExecuteCommand(Lobby2ServerCommand *command)
{
//SLNet::BitStream out;
if (command->lobby2Message->PrevalidateInput()==false)
{
SendMsg(command->lobby2Message, command->callerSystemAddresses);
if (command->deallocMsgWhenDone)
msgFactory->Dealloc(command->lobby2Message);
return;
}
if (command->lobby2Message->RequiresAdmin() && HasAdminAddress(command->callerSystemAddresses)==false)
{
command->lobby2Message->resultCode=L2RC_REQUIRES_ADMIN;
SendMsg(command->lobby2Message, command->callerSystemAddresses);
//SendUnifiedToMultiple(&out,packetPriority, RELIABLE_ORDERED, orderingChannel, command->callerSystemAddresses);
if (command->deallocMsgWhenDone)
msgFactory->Dealloc(command->lobby2Message);
return;
}
if (command->lobby2Message->RequiresRankingPermission() && HasRankingAddress(command->callerSystemAddresses)==false)
{
command->lobby2Message->resultCode=L2RC_REQUIRES_ADMIN;
SendMsg(command->lobby2Message, command->callerSystemAddresses);
//SendUnifiedToMultiple(&out,packetPriority, RELIABLE_ORDERED, orderingChannel, command->callerSystemAddresses);
if (command->deallocMsgWhenDone)
msgFactory->Dealloc(command->lobby2Message);
return;
}
if (command->lobby2Message->ServerPreDBMemoryImpl(this, command->callingUserName)==true)
{
SendMsg(command->lobby2Message, command->callerSystemAddresses);
if (command->deallocMsgWhenDone)
msgFactory->Dealloc(command->lobby2Message);
return;
}
command->server=this;
AddInputCommand(*command);
}
void Lobby2Server::SetRoomsPlugin(RoomsPlugin *rp)
{
roomsPlugin=rp;
roomsPluginAddress=UNASSIGNED_SYSTEM_ADDRESS;
}
void Lobby2Server::SetRoomsPluginAddress(SystemAddress address)
{
roomsPluginAddress=address;
roomsPlugin=0;
}
void Lobby2Server::ClearUsers(void)
{
unsigned int i;
for (i=0; i < users.Size(); i++)
SLNet::OP_DELETE(users[i], __FILE__, __LINE__);
users.Clear(false, __FILE__, __LINE__);
}
void Lobby2Server::LogoffFromRooms(User *user)
{
// Remove from the room too
#if defined(__INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN)
// Tell the rooms plugin about the logoff event
if (roomsPlugin)
{
roomsPlugin->LogoffRoomsParticipant(user->userName, UNASSIGNED_SYSTEM_ADDRESS);
}
else if (roomsPluginAddress!=UNASSIGNED_SYSTEM_ADDRESS)
{
SLNet::BitStream bs;
RoomsPlugin::SerializeLogoff(user->userName,&bs);
SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, roomsPluginAddress, false);
}
#endif
}
void Lobby2Server::SendRemoteLoginNotification(SLNet::RakString handle, const DataStructures::List<SystemAddress>& recipients)
{
Notification_Client_RemoteLogin notification;
notification.handle=handle;
notification.resultCode=L2RC_SUCCESS;
SendMsg(&notification, recipients);
}
void Lobby2Server::OnLogin(Lobby2ServerCommand *command, bool calledFromThread)
{
if (calledFromThread)
{
ThreadAction ta;
ta.action=L2MID_Client_Login;
ta.command=*command;
threadActionQueueMutex.Lock();
threadActionQueue.Push(ta, __FILE__, __LINE__ );
threadActionQueueMutex.Unlock();
return;
}
bool objectExists;
unsigned int insertionIndex = users.GetIndexFromKey(command->callingUserName, &objectExists);
if (objectExists)
{
User * user = users[insertionIndex];
if (user->allowMultipleLogins==false)
{
SendRemoteLoginNotification(user->userName, user->systemAddresses);
LogoffFromRooms(user);
// Already logged in from this system address.
// Delete the existing entry, which will be reinserted.
SLNet::OP_DELETE(user,_FILE_AND_LINE_);
users.RemoveAtIndex(insertionIndex);
}
else
{
if (user->systemAddresses.GetIndexOf(command->callerSystemAddresses[0])==(unsigned int) -1)
{
// Just add system address and guid already in use to the list for this user
user->systemAddresses.Push(command->callerSystemAddresses[0], __FILE__, __LINE__);
user->guids.Push(command->callerGuids[0], __FILE__, __LINE__);
}
return;
}
}
else
{
// Different username, from the same IP address or RakNet instance
unsigned int idx2 = GetUserIndexByGUID(command->callerGuids[0]);
unsigned int idx3 = GetUserIndexBySystemAddress(command->callerSystemAddresses[0]);
if (idx2!=(unsigned int) -1)
{
User * user = users[idx2];
if (user->allowMultipleLogins==true)
return;
SendRemoteLoginNotification(user->userName, user->systemAddresses);
LogoffFromRooms(user);
SLNet::OP_DELETE(user,__FILE__,__LINE__);
users.RemoveAtIndex(idx2);
insertionIndex = users.GetIndexFromKey(command->callingUserName, &objectExists);
}
else if (idx3!=(unsigned int) -1)
{
User * user = users[idx3];
if (user->allowMultipleLogins==true)
return;
SendRemoteLoginNotification(user->userName, user->systemAddresses);
LogoffFromRooms(user);
SLNet::OP_DELETE(user,__FILE__,__LINE__);
users.RemoveAtIndex(idx3);
insertionIndex = users.GetIndexFromKey(command->callingUserName, &objectExists);
}
}
User *user = SLNet::OP_NEW<User>( __FILE__, __LINE__ );
user->userName=command->callingUserName;
user->systemAddresses=command->callerSystemAddresses;
user->guids=command->callerGuids;
user->callerUserId=command->callerUserId;
user->allowMultipleLogins=((Client_Login*)command->lobby2Message)->allowMultipleLogins;
users.InsertAtIndex(user, insertionIndex, __FILE__, __LINE__ );
#if defined(__INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN)
// Tell the rooms plugin about the login event
if (roomsPlugin)
{
roomsPlugin->LoginRoomsParticipant(user->userName, user->systemAddresses[0], user->guids[0], UNASSIGNED_SYSTEM_ADDRESS);
}
else if (roomsPluginAddress!=UNASSIGNED_SYSTEM_ADDRESS)
{
SLNet::BitStream bs;
RoomsPlugin::SerializeLogin(user->userName,user->systemAddresses[0], user->guids[0], &bs);
SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, roomsPluginAddress, false);
}
#endif
}
void Lobby2Server::OnLogoff(Lobby2ServerCommand *command, bool calledFromThread)
{
if (calledFromThread)
{
ThreadAction ta;
ta.action=L2MID_Client_Logoff;
ta.command=*command;
threadActionQueueMutex.Lock();
threadActionQueue.Push(ta, __FILE__, __LINE__ );
threadActionQueueMutex.Unlock();
return;
}
RemoveUser(command->callingUserName);
}
void Lobby2Server::OnChangeHandle(Lobby2ServerCommand *command, bool calledFromThread)
{
if (calledFromThread)
{
ThreadAction ta;
ta.action=L2MID_Client_ChangeHandle;
ta.command=*command;
threadActionQueueMutex.Lock();
threadActionQueue.Push(ta, __FILE__, __LINE__ );
threadActionQueueMutex.Unlock();
return;
}
unsigned int i;
SLNet::RakString oldHandle;
for (i=0; i < users.Size(); i++)
{
if (users[i]->callerUserId==command->callerUserId)
{
oldHandle=users[i]->userName;
users[i]->userName=command->callingUserName;
break;
}
}
if (oldHandle.IsEmpty())
return;
#if defined(__INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN)
// Tell the rooms plugin about the handle change
if (roomsPlugin)
{
roomsPlugin->ChangeHandle(oldHandle, command->callingUserName);
}
else if (roomsPluginAddress!=UNASSIGNED_SYSTEM_ADDRESS)
{
SLNet::BitStream bs;
RoomsPlugin::SerializeChangeHandle(oldHandle,command->callingUserName,&bs);
SendUnified(&bs,packetPriority, RELIABLE_ORDERED, orderingChannel, roomsPluginAddress, false);
}
#endif
}
void Lobby2Server::RemoveUser(RakString userName)
{
bool objectExists;
unsigned int index = users.GetIndexFromKey(userName, &objectExists);
if (objectExists)
RemoveUser(index);
}
void Lobby2Server::RemoveUser(unsigned int index)
{
User *user = users[index];
Lobby2ServerCommand command;
Notification_Friends_StatusChange *notification = (Notification_Friends_StatusChange *) GetMessageFactory()->Alloc(L2MID_Notification_Friends_StatusChange);
notification->otherHandle=user->userName;
notification->op=Notification_Friends_StatusChange::FRIEND_LOGGED_OFF;
notification->resultCode=L2RC_SUCCESS;
command.server=this;
command.deallocMsgWhenDone=true;
command.lobby2Message=notification;
command.callerUserId=user->callerUserId;
command.callingUserName=user->userName;
ExecuteCommand(&command);
unsigned i;
i=0;
threadPool.LockInput();
while (i < threadPool.InputSize())
{
command = threadPool.GetInputAtIndex(i);
if (command.lobby2Message->CancelOnDisconnect()&& command.callerSystemAddresses.Size()>0 && user->systemAddresses.GetIndexOf(command.callerSystemAddresses[0])!=(unsigned int)-1)
{
if (command.deallocMsgWhenDone)
SLNet::OP_DELETE(command.lobby2Message, __FILE__, __LINE__);
threadPool.RemoveInputAtIndex(i);
}
else
i++;
}
threadPool.UnlockInput();
LogoffFromRooms(user);
SLNet::OP_DELETE(user,__FILE__,__LINE__);
users.RemoveAtIndex(index);
}
unsigned int Lobby2Server::GetUserIndexBySystemAddress(SystemAddress systemAddress) const
{
unsigned int idx1,idx2;
for (idx1=0; idx1 < users.Size(); idx1++)
{
for (idx2=0; idx2 < users[idx1]->systemAddresses.Size(); idx2++)
{
if (users[idx1]->systemAddresses[idx2]==systemAddress)
return idx1;
}
}
return (unsigned int) -1;
}
unsigned int Lobby2Server::GetUserIndexByGUID(RakNetGUID guid) const
{
unsigned int idx1,idx2;
for (idx1=0; idx1 < users.Size(); idx1++)
{
for (idx2=0; idx2 < users[idx1]->guids.Size(); idx2++)
{
if (users[idx1]->guids[idx2]==guid)
return idx1;
}
}
return (unsigned int) -1;
}
unsigned int Lobby2Server::GetUserIndexByUsername(SLNet::RakString userName) const
{
unsigned int idx;
bool objectExists;
idx = users.GetIndexFromKey(userName,&objectExists);
if (objectExists)
return idx;
return (unsigned int) -1;
}
void Lobby2Server::StopThreads(void)
{
threadPool.StopThreads();
}
void Lobby2Server::SetConfigurationProperties(ConfigurationProperties c)
{
configurationProperties=c;
}
const Lobby2Server::ConfigurationProperties *Lobby2Server::GetConfigurationProperties(void) const
{
return &configurationProperties;
}
void Lobby2Server::GetUserOnlineStatus(UsernameAndOnlineStatus &userInfo) const
{
unsigned int idx = GetUserIndexByUsername(userInfo.handle);
if (idx!=-1)
{
userInfo.isOnline=true;
userInfo.presence=users[idx]->presence;
}
else
{
userInfo.isOnline=false;
userInfo.presence.status=Lobby2Presence::NOT_ONLINE;
userInfo.presence.isVisible=false;
}
}
void Lobby2Server::SetPresence(const SLNet::Lobby2Presence &presence, SLNet::RakString userHandle)
{
unsigned int index = GetUserIndexByUsername(userHandle);
if (index!=-1)
{
User *user = users[index];
user->presence=presence;
// Push notify presence update to friends
Lobby2ServerCommand command;
Notification_Friends_PresenceUpdate *notification = (Notification_Friends_PresenceUpdate *) GetMessageFactory()->Alloc(L2MID_Notification_Friends_PresenceUpdate);
notification->newPresence=presence;
notification->otherHandle=user->userName;
notification->resultCode=L2RC_SUCCESS;
command.server=this;
command.deallocMsgWhenDone=true;
command.lobby2Message=notification;
command.callerUserId=user->callerUserId;
command.callingUserName=user->userName;
ExecuteCommand(&command);
}
}
void Lobby2Server::GetPresence(SLNet::Lobby2Presence &presence, SLNet::RakString userHandle)
{
unsigned int userIndex = GetUserIndexByUsername(userHandle);
if (userIndex!=-1)
{
presence=users[userIndex]->presence;
}
else
{
presence.status=Lobby2Presence::NOT_ONLINE;
}
}
void Lobby2Server::SendUnifiedToMultiple( const SLNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char curOrderingChannel, const DataStructures::List<SystemAddress> systemAddresses )
{
for (unsigned int i=0; i < systemAddresses.Size(); i++)
SendUnified(bitStream,priority,reliability,curOrderingChannel,systemAddresses[i],false);
}