public bool ScheduleChange(MeshNetworkIdentity id, StateChange change, ref IDContainer idReference) { //Debug.Log("Schedule change: my identity has objectID = " + GetIdentity().GetObjectID()); if (GetIdentity().meshnetReference.database == null) { Debug.LogError("Can't schedule a state change without an active database"); return(false); } ushort transactionID = GetNextTransactionID(); callbackRegistry.Add(transactionID, idReference); callbackTimers.Add(transactionID, Time.time); StateChangeTransaction transaction = new StateChangeTransaction(transactionID, change, id); MeshPacket p = new MeshPacket(); p.SetContents(transaction.GetSerializedBytes()); p.SetPacketType(PacketType.DatabaseChangeRequest); //Debug.Log("Setting packet with source object ID = " + GetIdentity().GetObjectID()); p.SetSourceObjectId(GetIdentity().GetObjectID()); p.SetSourcePlayerId(GetIdentity().meshnetReference.GetLocalPlayerID()); p.SetTargetObjectId((ushort)ReservedObjectIDs.DatabaseObject); p.SetTargetPlayerId(netDB.GetIdentity().GetOwnerID()); GetIdentity().RoutePacket(p); return(true); }
public void HostGame() { if (lobby.Equals(CSteamID.Nil) == false) { Debug.LogError("Lobby already created. Probably already hosting. Must shut down hosting before doing it again."); return; } //Construct the network database. Very important! MeshNetworkIdentity databaseID = new MeshNetworkIdentity((ushort)ReservedObjectIDs.DatabaseObject, (ushort)ReservedPrefabIDs.Database, (ulong)GetLocalPlayerID(), true); database = game.SpawnDatabase(databaseID).GetComponent <NetworkDatabase>(); //Spawns the database prefab. Debug.Log("Registering database."); database.AddObject(databaseID, true); //Tells the database that it itself exists (funny) //First, we get our own player object, and we make ourselves the provider. Player me = ConstructPlayer(SteamUser.GetSteamID()); Debug.Log("Registering provider."); database.AddPlayer(me, true); //Actually create the lobby. Password info, etc, will be set after this. Debug.Log("Creating Lobby"); m_LobbyCreated.Set(SteamMatchmaking.CreateLobby(ELobbyType.k_ELobbyTypePrivate, 4)); }
public GameObject SpawnDatabase(MeshNetworkIdentity i) { if (meshnet == null) { Debug.LogError("Trying to spawn object when underlying mesh network not intialized."); return(null); } if (i.GetObjectID() != (ushort)ReservedObjectIDs.DatabaseObject) { Debug.LogError("Trying to use database spawning method for non-database object"); return(null); } i.SetMeshnetReference(meshnet); //set a reference to the mesh network if (networkPrefabs.ContainsKey(i.GetPrefabID()) == false) { Debug.LogError("NetworkPrefab registry error: Requested prefab ID does not exist."); return(null); } GameObject g = Instantiate(networkPrefabs[i.GetPrefabID()]); IdentityContainer c = g.GetComponent <IdentityContainer>(); if (c == null) { Debug.LogError("NetworkPrefab error: spawned prefab does not contain IdentityContainer"); return(null); } c.SetIdentity(i); activeObjects.Add(i.GetObjectID(), g); return(g); }
// Use this for initialization void Start() { thisTransform = transform; manager = FindObjectOfType <PhysicsGridManager>(); if (manager == null) { Debug.LogError("No grid manager found in scene"); } if (transform.parent != null) { SetGrid(transform.parent.GetComponent <PhysicsGrid>(), true); } else { SetGrid(manager.GetGridByID((ushort)ReservedObjectIDs.RootGrid), true); } if (parentGrid == null) { Debug.Log("ZonedTransform does not have a parent grid, this should happen rarely"); } debugRigidbody = GetComponent <Rigidbody>(); IdentityContainer c = GetComponent <IdentityContainer>(); if (c != null) { thisMNI = c.GetIdentity(); hasIdentityContainer = true; } manager.TriggerRootScan(); }
public DatabaseChangeResult RemoveObject(MeshNetworkIdentity i, bool publishChange) { if (objectList.ContainsKey(i.GetObjectID()) == false) { return(new DatabaseChangeResult(false, "Object does not exist")); } if (i.GetObjectID() == (ushort)ReservedObjectIDs.DatabaseObject) { Debug.LogError("Tried to remove database. Bad idea."); return(new DatabaseChangeResult(false, "Can't remove database.")); } objectList.Remove(i.GetObjectID()); if (publishChange) { if (GetAuthorized()) { SendObjectUpdate(i, StateChange.Removal); return(new DatabaseChangeResult(true, i.GetObjectID())); } else { Debug.LogError("Publishing changes not authorized."); return(new DatabaseChangeResult(false, "Publishing changes not authorized")); } } else { return(new DatabaseChangeResult(true, i.GetObjectID())); } }
//This simply instantiates a network prefab. //The objct should already exist on the database. public GameObject SpawnObject(ushort objectID) { if (meshnet == null) { Debug.LogError("Trying to spawn object when underlying mesh network not intialized."); return(null); } if (meshnet.database.LookupObject(objectID) == null) { Debug.LogError("Trying to spawn network object without presence on the database"); return(null); } MeshNetworkIdentity localIdentity = meshnet.database.LookupObject(objectID); localIdentity.SetMeshnetReference(meshnet); //set a reference to the mesh network if (networkPrefabs.ContainsKey(localIdentity.GetPrefabID()) == false) { Debug.LogError("NetworkPrefab registry error: Requested prefab ID does not exist."); return(null); } GameObject g = Instantiate(networkPrefabs[localIdentity.GetPrefabID()]); IdentityContainer c = g.GetComponent <IdentityContainer>(); if (c == null) { Debug.LogError("NetworkPrefab error: spawned prefab does not contain IdentityContainer"); return(null); } c.SetIdentity(localIdentity); activeObjects.Add(objectID, g); return(g); }
public DatabaseChangeResult ChangeObject(MeshNetworkIdentity i, bool publishChange) { if (objectList.ContainsKey(i.GetObjectID()) == false) { Debug.LogError("Object that was requested to be changed does not exist!"); return(new DatabaseChangeResult(false, "Object does not exist")); } if (i.GetObjectID() == (ushort)ReservedObjectIDs.DatabaseObject) { Debug.LogError("Tried to change database. This action is prohibited."); //maybe not in the future... return(new DatabaseChangeResult(false, "Can't change database")); } objectList[i.GetObjectID()].DeepCopyAndApply(i); if (publishChange) { if (GetAuthorized()) { SendObjectUpdate(i, StateChange.Change); return(new DatabaseChangeResult(true, i.GetObjectID())); } else { Debug.LogError("Publishing changes not authorized."); return(new DatabaseChangeResult(false, "Publishing changes not authorized")); } } else { return(new DatabaseChangeResult(true, i.GetObjectID())); } }
public void InitializeDatabaseClientside(MeshPacket p) { if (database != null) { Debug.LogError("Database already exists. InitializeDatabaseClientside prohibited."); return; } DatabaseUpdate u = DatabaseUpdate.ParseContentAsDatabaseUpdate(p.GetContents()); //Here, we construct the database shadow using the database update. bool flagHasFoundDatabase = false; MeshNetworkIdentity databaseID = null; foreach (MeshNetworkIdentity i in u.objectDelta.Keys) { if (i.GetObjectID() == (ushort)ReservedObjectIDs.DatabaseObject) { flagHasFoundDatabase = true; database = game.SpawnDatabase(i).GetComponent <NetworkDatabase>(); //Spawns the database prefab. databaseID = i; break; } } if (flagHasFoundDatabase == false || database == null) { Debug.LogError("Database intialization failed."); return; } database.ReceivePacket(p); }
//Performs a deep copy of the given MNI and applies it to the existing object, preserving active pointers //Preserves the existing MeshNetwork reference and the existing list of attached components //These are not serialized across the network (obviously) and thus should not be deepcopied public void DeepCopyAndApply(MeshNetworkIdentity i) { SetLocked(i.GetLocked()); SetObjectID(i.GetObjectID()); SetOwnerID(i.GetOwnerID()); SetPrefabID(i.GetPrefabID()); }
//Send delta containing an object private void SendObjectUpdate(MeshNetworkIdentity id, StateChange s) { Dictionary <Player, StateChange> playerListDelta = new Dictionary <Player, StateChange>(); Dictionary <MeshNetworkIdentity, StateChange> objectListDelta = new Dictionary <MeshNetworkIdentity, StateChange>(); objectListDelta.Add(id, s); SendDelta(playerListDelta, objectListDelta, (ulong)ReservedPlayerIDs.Broadcast, false); }
//Deserialize incoming byte data and construct a deserialized DatabaseUpdate //Note, this is static public static DatabaseUpdate ParseContentAsDatabaseUpdate(byte[] serializedData) { Dictionary <Player, StateChange> playerList = new Dictionary <Player, StateChange>(); Dictionary <MeshNetworkIdentity, StateChange> networkObjects = new Dictionary <MeshNetworkIdentity, StateChange>(); byte[] rawData = serializedData; byte numOfNewPlayers = rawData[0]; int pointer = 1; byte i = 0; while (i < numOfNewPlayers) { int blobLength = rawData[pointer]; pointer++; //pointer is now at the beginning of the player data blob byte[] playerData = new byte[blobLength]; Buffer.BlockCopy(rawData, pointer, playerData, 0, blobLength); Player p = Player.DeserializeFull(playerData); pointer += blobLength; StateChange s = (StateChange)rawData[pointer]; playerList.Add(p, s); pointer++; i++; } byte numOfObjects = rawData[pointer]; pointer++; //pointer now at the beginning of the first MeshNetworkIdentity data byte j = 0; while (j < numOfObjects) { MeshNetworkIdentity netid = new MeshNetworkIdentity(); byte[] trimmed = new byte[MeshNetworkIdentity.NETWORK_IDENTITY_BYTE_SIZE]; Buffer.BlockCopy(rawData, pointer, trimmed, 0, trimmed.Length); netid.DeserializeAndApply(trimmed); pointer += MeshNetworkIdentity.NETWORK_IDENTITY_BYTE_SIZE; StateChange s = (StateChange)rawData[pointer]; networkObjects.Add(netid, s); pointer++; j++; } ushort hash = BitConverter.ToUInt16(rawData, pointer); pointer += 2; bool fullUpdateFlag = BitConverter.ToBoolean(rawData, pointer); return(new DatabaseUpdate(playerList, networkObjects, hash, fullUpdateFlag)); }
void Update() { if (originTest) { originTest = false; Vector3 delta = new Vector3(1, 0, 1); currentWorldOrigin = currentWorldOrigin + delta; for (int i = 0; i < gridTransform.childCount; i++) { gridTransform.GetChild(i).localPosition -= delta; } } if (offsetSensor != null && (offsetSensor.position - gridTransform.position).magnitude > 1000) { Vector3 localDelta = gridTransform.worldToLocalMatrix * (offsetSensor.position - gridTransform.position); currentWorldOrigin = currentWorldOrigin + localDelta; for (int i = 0; i < gridTransform.childCount; i++) { gridTransform.GetChild(i).localPosition -= localDelta; } } if (thisMNI == null && hasGridID) { IdentityContainer c = gameObject.GetComponent <IdentityContainer>(); if (c == null && proxy != null) { c = proxy.GetComponent <IdentityContainer>(); } if (c == null) { thisMNI = null; } else { thisMNI = c.GetIdentity(); } } if (proxy != null && proxyZT != null) //if we have a proxy, and that proxy has a zone transform { gridZonedTransform.SetGrid(proxyZT.parentGrid, true); gridTransform.localPosition = proxyOffset + proxyZT.transform.localPosition; //gridTransform.localRotation = proxyZT.transform.localRotation; } else if (proxy != null) { gridTransform.position = proxyOffset + proxy.transform.position; //gridTransform.rotation = proxy.transform.rotation; } }
public static StateChangeTransaction ParseSerializedBytes(byte[] data) { StateChangeTransaction transaction = new StateChangeTransaction(); transaction.SetTransactionID(BitConverter.ToUInt16(data, 0)); transaction.SetChangeType((StateChange)data[2]); if (data[3] == 1) { byte[] dataEndCap = new byte[data.Length - 4]; Buffer.BlockCopy(data, 4, dataEndCap, 0, dataEndCap.Length); //hmmm MeshNetworkIdentity i = new MeshNetworkIdentity(); i.DeserializeAndApply(dataEndCap); transaction.SetObjectData(i); } return(transaction); }
public void WarmupHosting() { MeshNetworkIdentity databaseID = new MeshNetworkIdentity((ushort)ReservedObjectIDs.DatabaseObject, (ushort)ReservedPrefabIDs.Database, (ulong)GetLocalPlayerID(), true); NetworkDatabase database2 = game.SpawnDatabase(databaseID).GetComponent <NetworkDatabase>(); //Spawns the database prefab. Debug.Log("Registering database."); database2.AddObject(databaseID, true); //Tells the database that it itself exists (funny) //First, we get our own player object, and we make ourselves the provider. Player me = ConstructPlayer(SteamUser.GetSteamID()); Debug.Log("Registering provider."); database.AddPlayer(me, true); }
//Object modification methods public DatabaseChangeResult AddObject(MeshNetworkIdentity i, bool publishChange) { if (objectList.ContainsKey(i.GetObjectID())) { return(new DatabaseChangeResult(false, "Object already exists")); } if (i.GetObjectID() == (ushort)ReservedObjectIDs.DatabaseObject) { } //If the object isn't the database, we need to assign an available objectID if (i.GetObjectID() != (ushort)ReservedObjectIDs.DatabaseObject) { IDAssignmentResult idresult = GetAvailableObjectID(); //Look for an available object id if (idresult.success) { i.SetObjectID(idresult.id); } else { return(new DatabaseChangeResult(false, "No object id available for use. Too many objects?")); } } objectList.Add(i.GetObjectID(), i); if (publishChange) { if (GetAuthorized()) { SendObjectUpdate(i, StateChange.Addition); return(new DatabaseChangeResult(true, i.GetObjectID())); } else { Debug.LogError("Publishing changes not authorized."); return(new DatabaseChangeResult(false, "Publishing changes not authorized")); } } else { return(new DatabaseChangeResult(true, i.GetObjectID())); } }
IEnumerator SpawnCapsule() { float timeStart = Time.time; MeshNetworkIdentity requestedID = new MeshNetworkIdentity(0, prefabID, meshnet.GetLocalPlayerID(), false); IDContainer returnedObjectID = new IDContainer((ushort)ReservedObjectIDs.Unspecified); scheduler.ScheduleChange(requestedID, StateChange.Addition, ref returnedObjectID); while (returnedObjectID.id == (ushort)ReservedObjectIDs.Unspecified) { if (Time.time - timeStart > SPAWN_TIMEOUT) { Debug.LogError("Spawn timeout"); yield break; } yield return(new WaitForEndOfFrame()); } MeshNetworkIdentity newIdentity = meshnet.database.LookupObject(returnedObjectID.id); GameObject g = meshnet.game.GetObjectByIdentity(newIdentity.GetObjectID()); if (prefabID != 4) { g.AddComponent <BasicMovementTest>(); } yield break; yield return(new WaitForSeconds(1)); timeStart = Time.time; returnedObjectID = new IDContainer((ushort)ReservedObjectIDs.Unspecified); scheduler.ScheduleChange(newIdentity, StateChange.Removal, ref returnedObjectID); while (returnedObjectID.id == (ushort)ReservedObjectIDs.Unspecified) { if (Time.time - timeStart > SPAWN_TIMEOUT) { Debug.LogError("Spawn timeout"); yield break; } yield return(new WaitForEndOfFrame()); } }
public bool ScheduleChange(MeshNetworkIdentity id, StateChange change) { IDContainer dummyContainer = new IDContainer(); return(ScheduleChange(id, change, ref dummyContainer)); }
public void SetIdentity(MeshNetworkIdentity i) { thisIdentity = i; }
/* * Users SHOULD be able to: * Request object creation and assign the object to themselves * Request object ownership change of others' objects if the object is unlocked * Request object deletion of their own objects * * * Users SHOULD NOT be able to: * Request object deletion of others' objects * Create objects and make other players the owner * Request object ownership change of others' objects if the object is locked * Request changing objects' objectID, prefabID, or any other * */ public void ConsiderChangeRequest(MeshPacket p) { if (GetAuthorized() == false) { Debug.LogError("Being asked to change the database when not authorized!"); return; } if (LookupPlayer(p.GetSourcePlayerId()) == null) { Debug.LogError("Change request source player does not exist on the database"); return; } StateChangeTransaction transaction = StateChangeTransaction.ParseSerializedBytes(p.GetContents()); if (transaction.GetChangeType() == StateChange.Addition) { //Check if the desired owner is the packet sender if (p.GetSourcePlayerId() != transaction.GetObjectData().GetOwnerID()) { Debug.LogError("Requested object creation tries to assign to new player: prohibited"); return; } DatabaseChangeResult result = AddObject(transaction.GetObjectData(), true); if (result.success == false) { Debug.LogError("Requested object addition failed: " + result.error); return; } else { game.SpawnObject(result.data); EchoChangeRequest(transaction.GetTransactionID(), result.data, p.GetSourcePlayerId()); } } else if (transaction.GetChangeType() == StateChange.Removal) { //Check if requesting user owns the object if (p.GetSourcePlayerId() != transaction.GetObjectData().GetOwnerID()) { Debug.LogError("User trying to remove an object that doesn't belong to them"); return; } MeshNetworkIdentity localObjectToRemove = LookupObject(transaction.GetObjectData().GetObjectID()); if (localObjectToRemove == null) { Debug.LogError("Couldn't find the object requested to be removed."); return; } DatabaseChangeResult result = RemoveObject(LookupObject(transaction.GetObjectData().GetObjectID()), true); if (result.success == false) { Debug.LogError("Requested object removal failed: " + result.error); return; } else { game.RemoveObject(result.data); EchoChangeRequest(transaction.GetTransactionID(), localObjectToRemove.GetObjectID(), p.GetSourcePlayerId()); } } else if (transaction.GetChangeType() == StateChange.Change) { if (p.GetSourcePlayerId() != transaction.GetObjectData().GetOwnerID()) { Debug.LogError("User trying to change an object that doesn't belong to them"); return; } MeshNetworkIdentity localObjectToChange = LookupObject(transaction.GetObjectData().GetObjectID()); if (localObjectToChange == null) { Debug.LogError("Couldn't find the object requested to be changed."); return; } DatabaseChangeResult result = ChangeObject(LookupObject(transaction.GetObjectData().GetObjectID()), true); if (result.success == false) { Debug.LogError("Requested object change failed: " + result.error); return; } else { EchoChangeRequest(transaction.GetTransactionID(), localObjectToChange.GetObjectID(), p.GetSourcePlayerId()); } } }
public void SetIdentity(MeshNetworkIdentity id) { thisObjectIdentity = id; }
void ParseData(MeshPacket incomingPacket) { //Debug.Log("Packet parsing: type = " + incomingPacket.GetPacketType() + ", source playerID = " + incomingPacket.GetSourcePlayerId() + ", target objectID = " + incomingPacket.GetTargetObjectId()); if (incomingPacket.GetSourcePlayerId() == meshnet.GetLocalPlayerID()) { //Debug.Log("Discarding packet from self"); //return; } if (incomingPacket.GetPacketType() == PacketType.PlayerJoin) { Debug.Log("PlayerJoin packet identified"); if (meshnet.database == null) { Debug.LogError("Database not intialized yet!"); return; } if (meshnet.database.GetAuthorized() == false) { Debug.Log("I'm not the provider. Discarding PlayerJoin packet"); return; } CSteamID sID = new CSteamID(incomingPacket.GetSourcePlayerId()); Player p = meshnet.ConstructPlayer(sID); meshnet.database.AddPlayer(p, true); return; } else if (incomingPacket.GetPacketType() == PacketType.DatabaseUpdate) { if (meshnet.database == null) { Debug.Log("Received first database update, no database to send it to."); Debug.Log("Rerouting to MeshNetwork."); meshnet.InitializeDatabaseClientside(incomingPacket); return; } } else if (incomingPacket.GetPacketType() == PacketType.KickPacket) { meshnet.initiateDisconnect(); } //If the packet is neither a PlayerJoin or a DatabaseUpdate Player source = meshnet.database.LookupPlayer(incomingPacket.GetSourcePlayerId()); //retrieve which player sent this packet if (source == null) //hmmm, the NBD can't find the player { Debug.LogError("Player from which packet originated does not exist on local NDB."); return; } MeshNetworkIdentity targetObject = meshnet.database.LookupObject(incomingPacket.GetTargetObjectId()); if (targetObject == null) { Debug.LogError("Packet's target object doesn't exist on the database!"); //Debug.LogError("type = " + incomingPacket.GetPacketType() + ", sourceObject = " + incomingPacket.GetSourceObjectId() + ", source player = " + incomingPacket.GetSourcePlayerId() + ", target object = " + incomingPacket.GetTargetObjectId()); return; } targetObject.ReceivePacket(incomingPacket); }
public void SetObjectData(MeshNetworkIdentity i) { obj = i; }
public StateChangeTransaction() { transactionID = 0; changeType = StateChange.Unspecified; obj = null; }
public void SetIdentity(MeshNetworkIdentity id) { identity = id; PopulateComponents(); }
public StateChangeTransaction(ushort id, StateChange type, MeshNetworkIdentity i) { transactionID = id; changeType = type; obj = i; }
//Runs some checks to make sure that the serialization //systems are running and correctly translating the data. //TODO automate checking public static void DebugDatabaseSerialization() { Debug.Log("Creating player named Mary Jane."); Player p1 = new Player("Mary Janee", 2233443, "abcde"); Debug.Log("Creating player named John Smith"); Player p2 = new Player("John Smith", 52342342, "12345"); DatabaseUpdate db = new DatabaseUpdate(); db.playerDelta.Add(p1, StateChange.Addition); db.playerDelta.Add(p2, StateChange.Removal); db.isFullUpdate = true; MeshNetworkIdentity dummy1 = new MeshNetworkIdentity(); MeshNetworkIdentity dummy2 = new MeshNetworkIdentity(); dummy1.SetObjectID(1337); dummy1.SetOwnerID(1234); dummy1.SetLocked(true); dummy2.SetObjectID(4200); dummy2.SetOwnerID(4321); dummy2.SetLocked(false); db.objectDelta.Add(dummy1, StateChange.Change); db.objectDelta.Add(dummy2, StateChange.Addition); db.fullHash = 4321; Debug.Log("Total payload length: " + db.GetSerializedBytes().Length); //Debug.Log("Database hash: " + NetworkDatabase.GenerateDatabaseChecksum(db.playerDelta, db.objectDelta)); MeshPacket p = new MeshPacket(); p.SetPacketType(PacketType.DatabaseUpdate); p.SetSourceObjectId((byte)ReservedObjectIDs.DatabaseObject); p.SetSourcePlayerId(120); p.SetTargetObjectId((byte)ReservedObjectIDs.DatabaseObject); p.SetTargetPlayerId((byte)ReservedPlayerIDs.Broadcast); p.SetContents(db.GetSerializedBytes()); byte[] transmitData = p.GetSerializedBytes(); //THIS WOULD GET SENT ACROSS THE NETWORK MeshPacket received = new MeshPacket(transmitData); Debug.Log("Received packet:"); Debug.Log("packetType: " + received.GetPacketType()); Debug.Log("sourceObjectID: " + received.GetSourceObjectId()); Debug.Log("sourcePlayerID: " + received.GetSourcePlayerId()); Debug.Log("targetObjectID: " + received.GetTargetObjectId()); Debug.Log("targetPlayerID: " + received.GetTargetPlayerId()); Debug.Log("Payload length: " + received.GetContents().Length); DatabaseUpdate receivedDB = DatabaseUpdate.ParseContentAsDatabaseUpdate(received.GetContents()); Debug.Log("Received DatabaseUpdate: isfullupdate = " + receivedDB.isFullUpdate); Debug.Log("Hash = " + receivedDB.fullHash); //Debug.Log("Database hash: " + NetworkDatabase.GenerateDatabaseChecksum(db.playerDelta, db.objectDelta)); Debug.Log("Total number of objects: " + receivedDB.objectDelta.Count); int i = 1; foreach (MeshNetworkIdentity id in receivedDB.objectDelta.Keys) { Debug.Log("Object " + i + ": "); Debug.Log("objectID: " + id.GetObjectID()); Debug.Log("prefabID: " + id.GetPrefabID()); Debug.Log("ownerID : " + id.GetOwnerID()); Debug.Log("Locked: " + id.GetLocked()); i++; } Debug.Log("Total number of players: " + receivedDB.playerDelta.Count); i = 1; foreach (Player player in receivedDB.playerDelta.Keys) { Debug.Log("Player " + i + ": "); Debug.Log("Desanitized Name: " + player.GetNameDesanitized()); Debug.Log("Sanitized Name: " + player.GetNameSanitized()); Debug.Log("uniqueID: " + player.GetUniqueID()); Debug.Log("privateKey: " + player.GetPrivateKey()); i++; } }