private void ActOnMatching() { byte[] buf = DeserializeFrame(matchingState0, matchingState1); if (buf.Length < 5) { return; } int n = 0; int newMatchingDuration = 0; newMatchingDuration |= (int)buf[n++] << 8; newMatchingDuration |= (int)buf[n++]; MatchingDurationSeconds = newMatchingDuration; int time = 0; time |= (int)buf[n++] << 24; time |= (int)buf[n++] << 16; time |= (int)buf[n++] << 8; time |= (int)buf[n++]; lastSeenMatchingServerTimeMillis = time; int matchCount = buf[n++]; lastSeenMatchCount = matchCount; int[] matching = new int[matchCount * 2]; for (int i = 0; i < matchCount; i++) { int player1 = 0; player1 |= (int)buf[n++] << 8; player1 |= (int)buf[n++]; matching[i * 2] = player1; int player2 = 0; player2 |= (int)buf[n++] << 8; player2 |= (int)buf[n++]; matching[i * 2 + 1] = player2; } lastSeenMatching = matching; Log($"Deserialized new matching at {lastSeenMatchingServerTimeMillis}, with {matchCount}\n" + $"matchings: [{join(matching)}]"); VRCPlayerApi[] players = MatchingTracker.GetActivePlayers(); int myPlayerId = Networking.LocalPlayer.playerId; for (int i = 0; i < matchCount; i++) { if (matching[i * 2] == myPlayerId || matching[i * 2 + 1] == myPlayerId) { var other = matching[i * 2] == myPlayerId ? matching[i * 2 + 1] : matching[i * 2]; VRCPlayerApi otherPlayer = null; foreach (var pl in players) { if (pl.playerId == other) { otherPlayer = pl; break; } } if (otherPlayer == null) { Log($"found local player id={myPlayerId} matched with {other}, but {other} seems to have left, aborting.."); return; } // we're matched, teleport to the ith unoccupied room // divided by 2 since there are two people per match Log($"found local player id={myPlayerId} matched with id={other} name={otherPlayer.displayName}, teleporting to room {i}"); var p = privateRooms[i]; // record MatchingTracker.SetLocallyMatchedWith(otherPlayer, true); Vector3 adjust = matching[i * 2] == myPlayerId ? Vector3.forward : Vector3.back; // look at the center of the room Quaternion rotation = Quaternion.LookRotation(adjust * -1); // avoid lerping (apparently on by default) Networking.LocalPlayer.TeleportTo(adjust + p.transform.position, rotation, VRC_SceneDescriptor.SpawnOrientation.AlignPlayerWithSpawnPoint, lerpOnRemote: false); // teleport timer to location as visual if (CooldownBetweenMatchingSeconds > 0) { // make countdown slightly shorter than round time PrivateRoomTimer.StartCountdown((float)(MatchingDurationSeconds - CooldownBetweenMatchingSeconds)); PrivateRoomTimer.teleportAtCountdown = true; } else { // dont' teleport, and just let the code below teleport out once the new mathcing comes. PrivateRoomTimer.StartCountdown(MatchingDurationSeconds); PrivateRoomTimer.teleportAtCountdown = false; } PrivateRoomTimer.transform.position = p.transform.position; return; } } Log($"Local player id={myPlayerId} was not in the matching, teleporting out if they were in a room previously"); // if the player was previously in a room, the privateroomtimer is there with them and will teleport them out. PrivateRoomTimer.TeleportOut(); }
private void UpdateCanvas() { if (!MatchingTracker.started) { return; } if ((updateCooldown -= Time.deltaTime) > 0) { return; } updateCooldown = 1f; // show indication if player hasn't got ownership yet if (MatchingTracker.localPlayerState == null) { title.text = "Initializing, please wait warmly\n(If this persists, try rejoining.)"; return; } var matchesRemaining = 0; for (int i = 0; i < 80; i++) { MatchingTrackerPlayerState state = MatchingTracker.playerStates[i]; VRCPlayerApi p = state.GetExplicitOwner(); if (p == null || Networking.LocalPlayer == p) { toggles[i].gameObject.SetActive(false); activePlayerLastUpdate[i] = null; continue; } toggles[i].gameObject.SetActive(true); var wasMatchedWith = MatchingTracker.GetLocallyMatchedWith(p); var matchedWithUs = state.matchedWithLocalPlayer; if (wasMatchedWith) { texts[i].text = MatchingTracker.GetDisplayName(p); var seconds = Time.time - MatchingTracker.GetLastMatchedWith(p); var minutes = seconds / 60f; var hours = minutes / 60f; texts[i].text = $"{MatchingTracker.GetDisplayName(p)} (matched " + (hours > 1 ? $"{Mathf.FloorToInt(hours):D2}:{Mathf.FloorToInt(minutes):D2} ago)" : minutes > 1 ? $"{Mathf.FloorToInt(minutes):##} minutes ago)" : $"{Mathf.FloorToInt(seconds):##} seconds ago)"); } else if (matchedWithUs) { texts[i].text = $"{MatchingTracker.GetDisplayName(p)} (matched with you on their end)"; } else if (!state.matchingEnabled) { texts[i].text = $"{MatchingTracker.GetDisplayName(p)} (taking a break from matching)"; } else { texts[i].text = MatchingTracker.GetDisplayName(p); matchesRemaining++; } if (activePlayerLastUpdate[i] == MatchingTracker.GetDisplayName(p)) { // if player changed state in ui (doesn't match our internal state) if (toggles[i].isOn != lastSeenToggle[i]) { MatchingTracker.SetLocallyMatchedWith(p, toggles[i].isOn); } else { // set UI from tracker state toggles[i].isOn = wasMatchedWith; } lastSeenToggle[i] = toggles[i].isOn; } else { // wasn't the same player before activePlayerLastUpdate[i] = MatchingTracker.GetDisplayName(p); // set the UI state ignoring what it was toggles[i].isOn = wasMatchedWith; lastSeenToggle[i] = wasMatchedWith; } } title.text = $"Player Checklist ({matchesRemaining} matches remaining)"; }