private void StartMonitoringActors() { if (monitoringActors) { return; } monitoringActors = true; engine.onActorCreated += OnActorCreated; engine.onBeforeActorDestroy += OnActorDestroyed; foreach (VoosActor actor in engine.EnumerateActors()) { StartMonitoringActor(actor); } }
private void RefreshActorList() { List <VoosActor> onList = new List <VoosActor>(engine.EnumerateActors().Where(actor => ShouldActorBeListed(false, actor))); // Include a "None" option so the user can choose to fill in a field with "no actor". // A null in the list means None. onList.Insert(0, null); onStageList.SetActors(onList); onStageList.AddClickListener(OnActorClicked); if (allowOffstageActors) { offStageList.SetActors(engine.EnumerateActors().Where(actor => ShouldActorBeListed(true, actor))); offStageList.AddClickListener(OnActorClicked); } }
void NotifyCardRemovedLocal(string brainId, string useId) { if (!HasBrain(brainId)) { return; } // Check the useId is still on the brain.. var brain = GetBrain(brainId); if (!brain.HasUse(useId)) { // Ignore it - could be a reflected duplicate call. return; } foreach (VoosActor actor in voosEngine.EnumerateActors(). Where(actor => actor.GetBrainName() == brainId)) { // Only call for my local actors (remote owners will take care of calling this for their actors). if (actor.IsLocallyOwned()) { CallBehaviorUseMethod <object, object>( useId, actor.GetName(), "onCardRemoved", null); } } }
private void HandleModuleCompileError(VoosEngine.ModuleCompileError error) { string behaviorUri = error.moduleKey; var beh = behaviorSystem.GetBehaviorData(behaviorUri); var meta = JsonUtility.FromJson <BehaviorCards.CardMetadata>(beh.metadataJson); HashSet <string> usingBrainIds = new HashSet <string>(from entry in behaviorSystem.BrainsForBehavior(behaviorUri) select entry.id); VoosActor oneActor = (from actor in voosEngine.EnumerateActors() where usingBrainIds.Contains(actor.GetBrainName()) select actor).FirstOrDefault(); string actorsUsing = oneActor == null ? "No actors using it" : $"One actor using it: {oneActor.GetDebugName()}"; string msg = $"<color=yellow>Error with card '{meta.cardSystemCardData.title}' (line {error.lineNum}). {actorsUsing}. The error:</color>\n<color=red>{error.message}</color>"; CommandTerminal.HeadlessTerminal.Buffer.HandleLog(msg, TerminalLogType.Error, null); // NOTE: Ideally, we'd do this if we know the code editor isn't viewing this particular behavior if (Time.timeSinceLevelLoad < 5f && (!IsCodeEditorOpen() || error.lineNum == -1)) { popup.Show($"There was an error with card '{meta.cardSystemCardData.title}' (line {error.lineNum}):\n{error.message}\n<color=#666666>{actorsUsing}.</color>", "OK", null, 1400f); } }
public override void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer) { Util.Log($"Player {otherPlayer.ID} disconnected."); var cms = FindObjectOfType <HudNotifications>(); if (cms != null) { cms.AddMessage($"'{otherPlayer.NickName}' left"); } if (!PhotonNetwork.isMasterClient) { return; } // Inefficient and global, but whatever... foreach (UserBody body in FindObjectsOfType <UserBody>()) { if (body.GetComponent <PhotonView>().OwnerActorNr == otherPlayer.ID) { PhotonNetwork.Destroy(body.gameObject); } } // Reclaim all actors owned by this player. A bit surprised photon doesn't // do this for us.. foreach (VoosActor actor in voosEngine.EnumerateActors()) { if (actor.reliablePhotonView != null && actor.reliablePhotonView.OwnerActorNr == otherPlayer.ID) { // Why not call actor.RequestOwnership here? That would result in // nothing happening, because the other player is definitely gone. But // calling view.RequestOwnership() directly will go through the normal // Photon flow, bypassing all our logic, and Photon should just grant us // ownership. actor.reliablePhotonView.RequestOwnership(); } } // Broadcast a message saying the player disconnected. PlayerLeftMessage playerLeftMessage = new PlayerLeftMessage(); playerLeftMessage.id = otherPlayer.ID; playerLeftMessage.nickName = otherPlayer.NickName; voosEngine.BroadcastMessage("PlayerLeft", playerLeftMessage); }
public void OnGUI() { if (DebugLevel % 3 == 0 || PhotonNetwork.offlineMode) { return; } GUI.skin = this.Skin; int height = 600; GUILayout.BeginArea(new Rect(0, Screen.height / 2 - height / 2, 300, height)); string stateColorString = PhotonNetwork.inRoom ? "#00ff88" : "yellow"; GUILayout.Label($"<color={stateColorString}>{PhotonNetwork.connectionStateDetailed.ToString()}</color>"); if (PhotonNetwork.inRoom) { GUILayout.Label($"Room: {PhotonNetwork.room.name}"); System.Action <PhotonPlayer> emitLabel = (PhotonPlayer player) => { int localPlayerId = player.ID; string playerIsMaster = player.IsMasterClient ? "(master) " : ""; string you = player == PhotonNetwork.player ? "(you)" : ""; string playerLabel = Util.GetPlayerName(player.GetRoomIndex()); GUILayout.Label(string.Format("P{3} ({0}), {1} {2}{4}", localPlayerId, playerLabel, playerIsMaster, player.GetRoomIndex(), you)); }; emitLabel(PhotonNetwork.player); foreach (PhotonPlayer otherPlayer in PhotonNetwork.otherPlayers) { emitLabel(otherPlayer); } GUILayout.Label($"{PhotonNetwork.otherPlayers.Length} other players in room"); } if (unreliable != null) { var diag = unreliable.GetDiagnostics(); GUILayout.Label($"Unreliable diag:\n{JsonUtility.ToJson(diag, true)}"); } else { GUILayout.Label($"Unreliable DNE"); } GUILayout.EndArea(); if (DebugLevel % 3 == 2) { foreach (VoosActor actor in engine.EnumerateActors()) { DrawActorDebugGUI(actor); } } }
static void CommandActors(CommandArg[] args) { if (consoleInstance == null) { return; } string substring = args.Length > 0 ? args[0].ToString().ToLowerInvariant() : null; int i = 0; foreach (var actor in consoleInstance.EnumerateActors()) { if (substring == null || (actor.GetDisplayName() ?? "").ToLowerInvariant().Contains(substring)) { HeadlessTerminal.Log($"#{i}: {actor.GetDisplayName()} ({actor.GetName()})"); } i++; } }
// Update is called once per frame void Update() { if (!setupComplete) { return; } foreach (VoosActor actor in voosEngine.EnumerateActors()) { if (IsActorCenterOnScreen(actor)) { InWorldLogPanel panel = GetOrAddPanel(actor); UpdatePanelPosition(panel); } else { AttemptRemovePanel(actor); } } }
private void AssignPlayerToActorLocal(int playerNumber, string actorName) { foreach (VoosActor actor in engine.EnumerateActors()) { // Only act on locally owned actors (all clients are running this method by the miracle of RPCs, // so each client will act on the actors they own). if (!actor.IsLocallyOwned()) { continue; } Behaviors.BehaviorUse playerControlsPanel = TryGetPlayerControlsPanel(actor); if (playerControlsPanel == null) { continue; } if (playerNumber != ASSIGN_ACTOR_AS_NPC) { // If this actor is set to this player#, set it 0 unless it's the desired actor. // If this actor is the desired actor, set its player#. int thisPlayerNumber = playerControlsPanel.GetPropertyValue <int>(PLAYER_NUMBER_PROP_NAME, PLAYER_NUMBER_PROP_DEFAULT_VALUE); if (thisPlayerNumber == playerNumber && actor.GetName() != actorName) { SetPlayerNumberOnPlayerControlsPanel(actor, playerControlsPanel, 0); } else if (thisPlayerNumber != playerNumber && actor.GetName() == actorName) { SetPlayerNumberOnPlayerControlsPanel(actor, playerControlsPanel, playerNumber); } } else { // Our mission is just to make the desired actor an NPC. if (actor.GetName() == actorName) { DeletePlayerControlsPanel(actor); } } } }
private void RefreshDropdown() { dropdownActors = new List <VoosActor>(voosEngine.EnumerateActors()); ClearOptions(); // TODO wasteful to allocate a new array each time... List <string> actorNames = new List <string>(dropdownActors.Count); foreach (VoosActor dropdownActor in dropdownActors) { actorNames.Add(dropdownActor.GetDisplayName()); } AddOptions(actorNames); for (int i = 0; i < dropdownActors.Count; i++) { if (dropdownActors[i] == actor) { this.value = i; return; } } }
public void Reset( Vector2 worldSize, byte[] terrainV2Data, bool loadingDataFromBeforeDigging, byte[] legacyData, ulong[] customStyleWorkshopIds = null, string simpleData = null) { Debug.Assert(worldSize.magnitude > 0f); Util.Log($"Resetting terrain!"); float groundSizeX = worldSize.x; float groundSizeZ = worldSize.y; var newDims = new Int3( Mathf.CeilToInt(groundSizeX / BLOCK_SIZE.x), BlocksYCount, Mathf.CeilToInt(groundSizeZ / BLOCK_SIZE.z)); Debug.Assert(newDims >= new Int3(10, 10, 10)); Int3 newBotCenter = new Int3(newDims.x / 2, 0, newDims.z / 2); // If old data exists, make sure we restore it. This is the resize use case. byte[] restoreData = null; Int3 restoreDims = Int3.zero(); if (terrainV2 != null) { var oldDims = terrainV2.GetWorldDimensions(); restoreDims = Int3.Min(oldDims, newDims); Int3 start = oldDims / 2 - restoreDims / 2; Int3 end = start + restoreDims; Debug.Assert(start >= Int3.zero()); Debug.Assert(end <= oldDims); restoreData = terrainV2.Serialize(start, end); Destroy(terrainV2.gameObject); terrainV2 = null; } using (Util.Profile("terrainV2 instantiate")) terrainV2 = Instantiate(terrainV2Prefab, Vector3.zero, Quaternion.identity, this.transform); using (Util.Profile("terrainV2 SetWorldDimensions")) terrainV2.SetWorldDimensions(newDims); terrainV2.SetRootOffset(new Vector3( -newDims.x / 2 * 2.5f, BlocksYStart * BlockHeight + BlockHeight / 2f, -newDims.z / 2 * 2.5f)); using (Util.Profile("Create color terrain textures")) { int texSize = terrainV2.GetStyleTextureResolution(); // Color32 way faster in general than Color. for (int i = 0; i < NumSolidColorStyles; i++) { Color32[] pixels = new Color32[texSize * texSize]; Color32 color32 = rendering.blockColors[i]; for (int j = 0; j < pixels.Length; j++) { pixels[j] = color32; } terrainV2.SetStyleTextures(i, pixels); // color // 10 is orange if (i == (int)BlockStyle.SolidColor10) { fallbackTexture = pixels; } } } Debug.Assert(fallbackTexture != null, "Could not find fallbackTexture?"); // TODO why do we do these specifically? Are they not read in via the loop below? terrainV2.SetStyleTextures((int)BlockStyle.Stone, terrainV2Textures[4].GetPixels32()); // stone terrainV2.SetStyleTextures((int)BlockStyle.Space, terrainV2Textures[1].GetPixels32()); // metal terrainV2.SetStyleTextures((int)BlockStyle.Grass, terrainV2Textures[8].GetPixels32(), terrainV2Textures[7].GetPixels32(), terrainV2Textures[6].GetPixels32()); // grass terrainV2.SetStyleTextures((int)BlockStyle.SnowRock, terrainV2Textures[11].GetPixels32(), terrainV2Textures[10].GetPixels32(), terrainV2Textures[9].GetPixels32()); // snow foreach (object obj in Enum.GetValues(typeof(BlockStyle))) { BlockStyle style = (BlockStyle)obj; if ((int)style <= (int)BlockStyle.SnowRock) { // We hard code this above for now. continue; } Color32[] topOrAtlas = null; Color32[] side = null; Color32[] overflow = null; foreach (var tex in terrainV2Textures) { if (tex == null) { continue; } if (!tex.name.StartsWith(style.ToString().ToLowerInvariant())) { continue; } if (tex.name.EndsWith("-top") || tex.name == style.ToString().ToLowerInvariant()) { topOrAtlas = tex.GetPixels32(); } else if (tex.name.EndsWith("-side-ceiling")) { side = tex.GetPixels32(); } else if (tex.name.EndsWith("-overflow")) { overflow = tex.GetPixels32(); } } if (topOrAtlas == null) { Util.LogWarning($"Had to use fallback texture for terrain style {style}. side={side}, overflow={overflow}"); topOrAtlas = fallbackTexture; } if (side != null) { #if UNITY_EDITOR Debug.Assert(overflow != null, $"{style.ToString()} style has a side texture but not an overflow?"); #endif } else { if (overflow != null) { Util.LogWarning($"Style {style} had an overflow texture but not a side? IGNORING overflow."); overflow = null; } } terrainV2.SetStyleTextures((int)style, topOrAtlas, side, overflow); } // Custom styles this.customStyleWorkshopIds.Clear(); if (customStyleWorkshopIds != null) { this.customStyleWorkshopIds.AddRange(customStyleWorkshopIds); } UpdateCustomStyleWorkshopIds(); if (restoreData != null) { terrainV2.Deserialize(restoreData, (newDims / 2 - restoreDims / 2)); } if (legacyData != null) { LoadLegacyTerrainData(legacyData); // But move all the blocks to our new system. using (Util.Profile("legacySync")) { foreach (var args in database.EnumerateBlocks()) { terrainV2.SetCell(args.cell.ToInt3() + GetV2Offset(), (int)args.value.style, (int)args.value.blockType - 1, (int)args.value.direction); } } } if (terrainV2Data != null) { Util.Log($"loading v2 data of {terrainV2Data.Length} bytes"); using (Util.Profile("terrainV2 Deserialize")) terrainV2.Deserialize(terrainV2Data); // Legacy upgrade if (loadingDataFromBeforeDigging) { // The serialized data was before digging. We need to move it up, effectively. Debug.Assert(BlocksYStart < 0); // Copy... byte[] temp = terrainV2.Serialize( Int3.zero(), newDims.WithY(newDims.y + BlocksYStart)); // Move up.. terrainV2.Deserialize( temp, Int3.zero().WithY(-BlocksYStart)); // At this point, we actually have 2 copies of the terrain, offset by // some Y! heh. But the SetSlices call below will deal with that. } } if (loadingDataFromBeforeDigging) { // Fill in the ground. BlockStyle style = BlockStyle.Grass; switch (stage.GetGroundType()) { case GameBuilderStage.GroundType.Snow: style = BlockStyle.SnowRock; break; case GameBuilderStage.GroundType.SolidColor: case GameBuilderStage.GroundType.Space: case GameBuilderStage.GroundType.Grass: default: style = BlockStyle.Grass; break; } terrainV2.SetSlices(0, (0 - BlocksYStart), (int)style, 0, 0); } if (!simpleData.IsNullOrEmpty()) { byte[] zippedBytes = System.Convert.FromBase64String(simpleData); using (var zippedStream = new System.IO.MemoryStream(zippedBytes, 0, zippedBytes.Length)) using (var unzipped = new System.IO.Compression.GZipStream(zippedStream, System.IO.Compression.CompressionMode.Decompress)) using (System.IO.BinaryReader reader = new System.IO.BinaryReader(unzipped)) { int version = reader.ReadUInt16(); // Unused. Debug.Assert(version == 0, $"Unknown simpleData version: {version}"); uint numBlocks = reader.ReadUInt32(); Util.Log($"reading in {numBlocks} from simpleData"); for (int i = 0; i < numBlocks; i++) { short x = reader.ReadInt16(); short y = reader.ReadInt16(); short z = reader.ReadInt16(); byte shape = reader.ReadByte(); byte direction = reader.ReadByte(); ushort style = reader.ReadUInt16(); this.SetCellValue( new Cell(x, y, z), new CellValue { blockType = (BlockShape)shape, direction = (BlockDirection)direction, style = (BlockStyle)style }); } } } // Now mark chunks with actors in them as important foreach (var actor in engine.EnumerateActors()) { // Non-dynamic-physics actors don't need terrain to exist...well, less so. if (!actor.GetEnablePhysics()) { continue; } var pos = actor.GetSpawnPosition(); Int3 cell = GetContainingCell(pos).ToInt3(); terrainV2.ReportRigidbodyAt((cell + GetV2Offset())); } }