void ApplyTaskUpdate(TaskUpdateBundled taskUpdate) { // See if we have a task on this storypoint. StoryTask updateTask = GENERAL.GetTaskForPoint(taskUpdate.pointID); if (updateTask == null) { // If not, and we're a client, we create the task. // If we're the server, we ignore updates for task we no longer know about. if (GENERAL.AUTHORITY == AUTHORITY.LOCAL) { updateTask = new StoryTask(taskUpdate.pointID, SCOPE.GLOBAL); updateTask.ApplyUpdateMessage(taskUpdate); Log("Created an instance of global task " + updateTask.Instruction + " id " + updateTask.PointID); if (taskUpdate.pointID != "GLOBALS") { // Now find a pointer. StoryPointer updatePointer = GENERAL.GetStorylinePointerForPointID(taskUpdate.pointID); if (updatePointer == null) { updatePointer = new StoryPointer(); Log("Created a new pointer for task " + updateTask.Instruction); } updatePointer.PopulateWithTask(updateTask); Log("Populated pointer " + updatePointer.currentPoint.StoryLine + " with task " + updateTask.Instruction); DistributeTasks(new TaskArgs(updateTask)); } } } else { updateTask.ApplyUpdateMessage(taskUpdate); updateTask.scope = SCOPE.GLOBAL;//?? Verbose("Applied update to existing task " + updateTask.Instruction); } }
public StoryPointer(string pointID, SCOPE setScope) { // Create a pointer from a given point. Task to be added later. currentPoint = GENERAL.GetStoryPointByID(pointID); currentTask = null; status = POINTERSTATUS.EVALUATE; scope = setScope; // GENERAL.AddPointer(this); GENERAL.ALLPOINTERS.Add(this); // updateMessageSend = new PointerUpdateBundled(); // we'll reuse. }
void updateTaskInfo(StoryPointer pointer) { StoryTask theTask = pointer.currentTask; if (theTask == null || theTask.Pointer.pointerObject == null) { return; } if (theTask.Instruction != "wait") { //string displayText; string displayText = theTask.scope == SCOPE.GLOBAL ? "G| " : "L| "; // if (theTask.scope == SCOPE.GLOBAL) //{ // displayText = "G | "; //} //else //{ // displayText = "L | "; //} displayText = displayText + theTask.Instruction + " | "; // If a task has a value for "debug" we display it along with task description. string debugText; if (theTask.GetStringValue("debug", out debugText)) { displayText = displayText + debugText; } theTask.Pointer.deusText.text = displayText; theTask.Pointer.deusTextSuper.text = theTask.Pointer.currentPoint.StoryLine + " " + GENERAL.ALLPOINTERS.Count; } else { theTask.Pointer.deusTextSuper.text = theTask.Pointer.currentPoint.StoryLine; } }
void Update() { int t = 0; while (t < taskList.Count) { StoryTask task = taskList[t]; if (!GENERAL.ALLTASKS.Exists(at => at == task)) { Log("Removing task:" + task.Instruction); taskList.RemoveAt(t); } else { if (setTaskHandler != null) { if (setTaskHandler(task)) { task.signOff(ID); taskList.RemoveAt(t); } else { t++; } } else { task.signOff(ID); taskList.RemoveAt(t); if (!handlerWarning) { Warning("No handler registered."); handlerWarning = true; } } } } }
public bool HandleTask(StoryTask task) { bool done = false; switch (task.Instruction) { case "helloworld": Log("Hello world"); done = true; break; case "unknowntask": break; default: done = true; break; } return(done); }
void handleTasks() { int t = 0; while (t < taskList.Count) { StoryTask task = taskList[t]; // if (task.pointer.getStatus () == POINTERSTATUS.KILLED && task.description != "end") { // if (task.pointer.getStatus () == POINTERSTATUS.KILLED && task.description != "end") { if (!GENERAL.ALLTASKS.Exists(at => at == task)) { Log("Removing task:" + task.Instruction); taskList.RemoveAt(t); } else { switch (task.Instruction) { case "debugon": DeusCanvas.SetActive(true); task.signOff(ID); taskList.RemoveAt(t); break; case "debugoff": DeusCanvas.SetActive(false); task.signOff(ID); taskList.RemoveAt(t); break; case "toggledebug": case "debugtoggle": DeusCanvas.SetActive(!DeusCanvas.activeSelf); task.signOff(ID); taskList.RemoveAt(t); break; /* * case "end": * * // after finishing the end task, we mark the pointer as killed, so it gets removed. * * task.pointer.setStatus (POINTERSTATUS.KILLED); * * * * // Log.Message ("Destroying ui for pointer for storyline " + task.pointer.currentPoint.storyLineName); * // * // // update first. some pointers reach end in a single go - we want those to be aligned. * // * // updateTaskDisplay (task); * // * // pointerList.Remove (task.pointer); * // * // pointerPositions [task.pointer.position] = null; * // * // Destroy (task.pointer.pointerObject); * // * // // after finishing the end task, we mark the pointer as killed, so it gets removed. * // * // task.pointer.setStatus (POINTERSTATUS.KILLED); * * // updateTaskDisplay (task); * * * task.signOff (me); * taskList.RemoveAt (t); * break; * */ default: // updateTaskDisplay (task); task.signOff(ID); taskList.RemoveAt(t); break; } } } }
void Update() { #if UNITY_EDITOR && UNITY_IOS // Pause applicatin simulation for debugging. if (Input.GetKeyDown("p")) { OnApplicationPause(true); } if (Input.GetKeyDown("o")) { OnApplicationPause(false); } if (state == STATE.PAUSED) { return; } #endif #if !SOLO if (displayNetworkGUIState()) { SetNetworkIndicators(); } #endif int t = 0; while (t < taskList.Count) { StoryTask task = taskList[t]; if (task.Instruction == "end") { Log("Encountered end task, removing pointer " + task.Pointer.currentPoint.StoryLine); GENERAL.ALLPOINTERS.Remove(task.Pointer); } if (!GENERAL.ALLTASKS.Exists(at => at == task)) { // Task was removed, so stop executing it. Log("Removing task:" + task.Instruction); taskList.RemoveAt(t); } else { // Task needs to be executed. // first check for storyengine tasks. bool done = false; switch (task.Instruction) { #if !SOLO // ---------------------------- VISUAL DEBUGGING ---------------------------- case "debugon": // Show network info. displayNetworkGUI(true); done = true; break; case "debugoff": // Hide network info. displayNetworkGUI(false); done = true; break; case "toggledebug": // Toggle network info. displayNetworkGUI(!displayNetworkGUIState()); done = true; break; // ---------------------------- SCOPE ---------------------------- case "isglobal": if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL) { task.Pointer.scope = SCOPE.GLOBAL; } done = true; break; case "islocal": task.Pointer.SetLocal(); //Verbose("Setting pointer scope to local: " + task.Pointer.currentPoint.StoryLine); done = true; break; // ---------------------------- SERVER SIDE ---------------------------- case "startserver": // Start broadcast and network server if not already going. if (!sending) { Log("Starting broadcast server, key " + ConnectionKey + " message " + ConnectionMessage); startBroadcastServer(ConnectionKey, ConnectionMessage); } if (!serving) { Log("Starting network server."); startNetworkServer(); } done = true; break; case "stopserver": // Stop broadcast and network server Log("Stopping broadcast server."); stopBroadcast(); Log("Stopping network server."); stopNetworkServer(); done = true; break; case "monitorconnections": if (NetworkObject == null || state == STATE.PAUSED) { break; } // Keep servers up on IOS (because of application pause) #if UNITY_IOS if (!sending) { Log("Starting broadcast server, key " + ConnectionKey + " message " + ConnectionMessage); startBroadcastServer(ConnectionKey, ConnectionMessage); } if (!serving) { Log("Starting network server."); startNetworkServer(); } #endif // Watch for new clients. Stays live indefinitely. if (serving && NewTrackedAddresses()) { // new addresses connected since last call foreach (string add in NewConnectedAddresses()) { Log("New client at " + add); } task.setCallBack("addclient"); } task.SetStringValue("debug", "clients: " + TrackedConnectedAddresses().Count); WasConnected = TrackedConnectedAddresses().Count > 0; break; case "pushglobaltasks": // go over all pointers and mark everything as modified // that way it'll get sent. // with single client apps this is ok, but for multiple clients we need something more advanced Log("Pushing global tasks."); foreach (StoryTask theTask in GENERAL.ALLTASKS) { if (theTask.scope == SCOPE.GLOBAL) { theTask.MarkAllAsModified(); } } done = true; break; // ---------------------------- CLIENT SIDE ---------------------------- case "serversearch": if (NetworkObject == null || state == STATE.PAUSED) { break; } if (listening) { if (foundServer()) { Log("Found broadcast server " + networkBroadcast.serverAddress + " key " + networkBroadcast.broadcastKey); stopBroadcast(); task.setCallBack("serverfound"); listening = false; done = true; } } else { startBroadcastClient(ConnectionKey); Log("Restarting broadcast listening for key " + ConnectionKey); listening = true; } break; case "startclient": string ServerAddress = networkBroadcast.serverAddress; if (ServerAddress != "") { Log("Starting network client for remote server " + ServerAddress); startNetworkClient(ServerAddress); } else { Warning("Trying to start client without server address."); } done = true; break; case "stopclient": Log("Stopping network client."); StopNetworkClient(); WasConnected = false; done = true; break; case "monitorserver": // Watches the connection, stays active until connection lost. if (clientIsConnected()) { WasConnected = true; } else { if (WasConnected) { // Lost connection. Restart server search. Log("Lost server connection."); StopNetworkClient(); WasConnected = false; task.setCallBack("client"); done = true; } else { // } } break; #endif default: // Not a controller task, pass it on. if (dataTaskHandler != null) { done = dataTaskHandler(task); } else { // If no handler available we just sign off, but issue a warning once. done = true; if (!handlerWarning) { Warning("No handler registered."); handlerWarning = true; } } break; } if (done) { task.signOff(ID); taskList.RemoveAt(t); } else { t++; } } } }
public StoryTask SpawnTask() { currentTask = new StoryTask(this); //status = POINTERSTATUS.EVALUATE; return(currentTask); }
public TaskArgs(StoryTask task) : base() // extend the constructor { theTasks = new List <StoryTask>(); theTasks.Add(task); }
void LateUpdate() { StoryUpdate storyUpdate = new StoryUpdate(); // Contains a collection of task and pointer updates. // Check our loadbalance. If we're sending too many updates we'll randomly drop frames. // All changes will be sent but if values are updated in the meantime the previous value will never be sent. // This is ok for running values but not ok for status values etc. int QueueSize = 0; if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL && NetworkServer.active) { // We're an active server. byte error; QueueSize = NetworkTransport.GetOutgoingMessageQueueSize(NetworkServer.serverHostId, out error); // debugValue.text = "queued out server: " + QueueSize; if ((NetworkError)error != NetworkError.Ok) { Error("Networktransport error: " + (NetworkError)error); } } if (GENERAL.AUTHORITY == AUTHORITY.LOCAL && networkManager != null && networkManager.client != null) { // We're an active client. byte error = (byte)NetworkError.Ok; if (networkManager.client.connection != null) { QueueSize = NetworkTransport.GetOutgoingMessageQueueSize(networkManager.client.connection.hostId, out error); } else { Warning("Can't get queue size (yet)"); } // debugValue.text = "queued out client: " + QueueSize; if ((NetworkError)error != NetworkError.Ok) { Error("Networktransport error: " + (NetworkError)error); } } switch (QueueSize) { case 0: BufferStatusOut = 0; break; case 1: case 2: BufferStatusOut = 1; break; default: BufferStatusOut = 2; break; } // forcing always send. // this means that a storyupdate is sent for every frame and they all arrive in order. // they get executed in order as well, but they may be batched together. // so multiple tasks might get executed in a single frame. they will be executed in the correct order. if (QueueSize < 3 || true) { // Iterate over all pointers to see if any were killed. Clients do not kill pointers themselves. // For consistency of network logic, local pointers that were killed are disposed by the director. // Global pointers are disposed here, after updating clients about them. for (int p = GENERAL.ALLPOINTERS.Count - 1; p >= 0; p--) { StoryPointer pointer = GENERAL.ALLPOINTERS[p]; if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL && pointer.scope == SCOPE.GLOBAL && pointer.GetStatus() == POINTERSTATUS.KILLED) //if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL && pointer.scope == SCOPE.GLOBAL && pointer.modified && pointer.GetStatus() == POINTERSTATUS.KILLED) { Log("Sending pointer kill update to clients: " + pointer.currentPoint.StoryLine); storyUpdate.AddStoryPointerUpdate(pointer.GetUpdate()); // bundled //pointer.modified = false; Log("Removing pointer " + pointer.currentPoint.StoryLine); GENERAL.ALLPOINTERS.Remove(pointer); } } // Iterate over all tasks. for (int i = GENERAL.ALLTASKS.Count - 1; i >= 0; i--) { StoryTask task = GENERAL.ALLTASKS[i]; // Cleanup completed tasks. if (task.getStatus() == TASKSTATUS.COMPLETE) { GENERAL.ALLTASKS.RemoveAt(i); Verbose("Task " + task.Instruction + " on storyline " + task.Pointer.currentPoint.StoryLine + " completed, removed from alltasks. "); } if (task.modified) { // Debugging: if a pointer is in the process of being killed, we may want to not send task updates // as they might result in the task being recreated clientside. if (task.Pointer.GetStatus() == POINTERSTATUS.KILLED) { Warning("Supressing sending task update for task with pointer that is dying. " + task.Instruction); } else { // Check if we need to send network updates. switch (GENERAL.AUTHORITY) { case AUTHORITY.LOCAL: if (task.scope == SCOPE.GLOBAL) { Verbose("Global task " + task.Instruction + " changed, adding to update for server."); storyUpdate.AddTaskUpdate(task.GetUpdateBundled()); // bundled } break; case AUTHORITY.GLOBAL: if (task.scope == SCOPE.GLOBAL) { Verbose("Global task " + task.Instruction + " changed, adding to update for clients."); storyUpdate.AddTaskUpdate(task.GetUpdateBundled()); // bundled } break; default: break; } task.modified = false; } } } // If anything to send, send. if (storyUpdate.AnythingToSend()) { switch (GENERAL.AUTHORITY) { case AUTHORITY.LOCAL: SendStoryUpdateToServer(storyUpdate); //Debug.Log("Sending story update to server. \n" + storyUpdate.DebugLog); break; case AUTHORITY.GLOBAL: SendStoryUpdateToClients(storyUpdate); //Debug.Log("Sending story update to clients. \n" + storyUpdate.DebugLog); //Debug.Log(storyUpdate.ToString()); break; default: break; } } } else { Warning("Dropping update."); } }
void Update() { #if !SOLO // Handle story updates, aiming for 1 per frame, assuming we're tyring to run in sync. // Lowest numbers are oldest. int UpdateCount = StoryUpdateStack.Count; switch (UpdateCount) { case 0: BufferStatusIn = 0; break; case 1: // exactly one, so apply. ApplyStoryUpdate(StoryUpdateStack[0]); StoryUpdateStack.RemoveAt(0); BufferStatusIn = 1; break; case 2: // Two, normally for different frames that happened to arrive in the same frame on this end. // Apply the oldest one, keep the other because we exact 0 updates during our next frame. ApplyStoryUpdate(StoryUpdateStack[0]); StoryUpdateStack.RemoveAt(0); BufferStatusIn = 1; break; default: // More than 2. Apply all the older ones in order of arrival, keep latest one. Warning("Update buffer >2"); BufferStatusIn = 2; while (StoryUpdateStack.Count > 1) { ApplyStoryUpdate(StoryUpdateStack[0]); StoryUpdateStack.RemoveAt(0); } break; } #endif switch (theDirector.status) { case DIRECTORSTATUS.ACTIVE: // Verbose("Director active ."); foreach (StoryTask task in GENERAL.ALLTASKS) { if (task.getCallBack() != "") { // if a callback was set (somewhere on the network) we act on it only if we are the server or if the task is local. if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL || task.scope == SCOPE.LOCAL) { task.Pointer.SetStatus(POINTERSTATUS.TASKUPDATED); } } } theDirector.EvaluatePointers(); List <StoryTask> newTasks = new List <StoryTask>(); for (int p = 0; p < GENERAL.ALLPOINTERS.Count; p++) { StoryPointer pointer = GENERAL.ALLPOINTERS[p]; switch (pointer.scope) { case SCOPE.GLOBAL: // If pointer scope is global, we add a task if our own scope is global as well. (If our scope is local, we'll be receiving the task over the network) if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL) { if (pointer.GetStatus() == POINTERSTATUS.NEWTASK) { pointer.SetStatus(POINTERSTATUS.PAUSED); StoryTask task = new StoryTask(pointer, SCOPE.GLOBAL); task.LoadPersistantData(pointer); newTasks.Add(task); task.modified = true; Verbose("Created global task " + task.Instruction + " for pointer " + pointer.currentPoint.StoryLine); } } break; case SCOPE.LOCAL: default: // If pointer scope is local, check if new tasks have to be generated. if (pointer.GetStatus() == POINTERSTATUS.NEWTASK) { pointer.SetStatus(POINTERSTATUS.PAUSED); StoryTask task = new StoryTask(pointer, SCOPE.LOCAL); task.LoadPersistantData(pointer); newTasks.Add(task); Verbose("Created local task " + task.Instruction + " for pointer " + pointer.currentPoint.StoryLine); } break; } } if (newTasks.Count > 0) { DistributeTasks(new TaskArgs(newTasks)); // if any new tasks call an event, passing on the list of tasks to any handlers listening } break; case DIRECTORSTATUS.READY: GENERAL.SIGNOFFS = eventHandlerCount(); if (GENERAL.SIGNOFFS == 0) { Error("No handlers registred. Pausing director."); theDirector.status = DIRECTORSTATUS.PAUSED; } else { Verbose("" + GENERAL.SIGNOFFS + " handlers registred."); Log("Starting storyline " + launchStoryline); theDirector.NewStoryLine(launchStoryline); theDirector.status = DIRECTORSTATUS.ACTIVE; } break; case DIRECTORSTATUS.NOTREADY: if (script != null) { theDirector.loadScript(script); break; } //if (scriptName != "") //{ // Warning("Please use the TextAsset field for your script."); // theDirector.loadScript(scriptName); // break; //} Error("No script reference found."); break; default: break; } }
void updateTaskDisplays() { if (PointerBlock == null || DebugCanvas == null) { Warning("Set references for pointerblock and deuscanvas."); return; } // Go over all pointers and plot them into our diplay for (int i = 0; i < GENERAL.ALLPOINTERS.Count; i++) { StoryPointer pointer = GENERAL.ALLPOINTERS[i]; if (!itemList.Exists(x => x.StoryPointer == pointer)) { Item ni = new Item(pointer, PointerBlock); ni.displayObject.transform.SetParent(DebugCanvas.transform, false); itemList.Add(ni); } } // Log("datacount " + GENERAL.ALLDATA.Count); // Go over all data objects and plot them into our diplay /* * for (int i = 0; i < GENERAL.ALLDATA.Count; i++) * { * * StoryData data = GENERAL.ALLDATA[i]; * * if (!itemList.Exists(x => x.StoryData == data)) * { * // Log("addin gitem"); * Item ni = new Item(data, PointerBlock); * ni.displayObject.transform.SetParent(DeusCanvas.transform, false); * * itemList.Add(ni); * * } * * } */ // Go over all items backwards and remove any for (int i = itemList.Count - 1; i >= 0; i--) { if (itemList[i].TimeOut != 0 && itemList[i].TimeOut < Time.time) { Destroy(itemList[i].displayObject); itemList.RemoveAt(i); } } // Go over all display items and update them. for (int i = 0; i < itemList.Count; i++) { int row = i / PointerDisplayCols; int col = i % PointerDisplayCols; float scale = 1f / PointerDisplayCols; Item item = itemList[i]; // item.displayObject.GetComponent<RectTransform>().localPosition = scale * new Vector3(PointerDisplayCols / 2f * -1024f + 512f + col * 1024f, PointerDisplayRows / 2f * 512f - 256f - row * 512f, 0); item.displayObject.GetComponent <RectTransform>().localPosition = scale * new Vector3(PointerDisplayCols / 2f * -1024f + 512f + col * 1024f, PointerDisplayRows / 2f * 512f - 256f - row * 512f, 0); item.displayObject.GetComponent <RectTransform>().localScale = new Vector3(scale, scale, scale); switch (item.Type) { case Item.TYPE.POINTER: if (item.TimeOut == 0 && !GENERAL.ALLPOINTERS.Contains(item.StoryPointer)) { item.TimeOut = Time.time + 1f; } StoryTask theTask = item.StoryPointer.currentTask; if (theTask == null || item.displayObject == null) { return; } if (theTask.Instruction != "wait" && theTask.Instruction != "end") { // string displayText = theTask.scope == SCOPE.GLOBAL ? "G| " : "L| "; string displayText = theTask.Instruction; // If a task has a value for "debug" we display it along with task description. string debugText; if (theTask.GetStringValue("debug", out debugText)) { displayText = displayText + " | " + debugText; } item.deusText.text = displayText; item.deusTextSuper.text = theTask.Pointer.currentPoint.StoryLine + " " + GENERAL.ALLPOINTERS.Count + (theTask.scope == SCOPE.GLOBAL ? " G" : " L"); } else { item.deusTextSuper.text = theTask.Pointer.currentPoint.StoryLine; } break; case Item.TYPE.DATA: /* * item.deusText.text = item.StoryData.GetChangeLog(); * item.deusTextSuper.text = "data: " + item.StoryData.ID + GENERAL.ALLDATA.Count; */ break; default: break; } } }
public bool HandleTask(StoryTask task) { bool done = false; switch (task.Instruction) { case "pause1": float timeOut1; if (!task.GetFloatValue("timeOut", out timeOut1)) { task.SetFloatValue("timeOut", Time.time + 1f); } else { if (Time.time > timeOut1) { done = true; } } break; case "pause5": float timeOut5; if (!task.GetFloatValue("timeOut", out timeOut5)) { task.SetFloatValue("timeOut", Time.time + 5f); } else { if (Time.time > timeOut5) { done = true; } } break; case "pause10": float timeOut10; if (!task.GetFloatValue("timeOut", out timeOut10)) { task.SetFloatValue("timeOut", Time.time + 5f); } else { if (Time.time > timeOut10) { done = true; } } break; case "debugon": GENERAL.Debugging = true; //DeusCanvas.SetActive(true); done = true; break; case "debugoff": GENERAL.Debugging = false; //DeusCanvas.SetActive(false); done = true; break; case "toggledebug": case "debugtoggle": GENERAL.Debugging = !GENERAL.Debugging; //DeusCanvas.SetActive(!DeusCanvas.activeSelf); done = true; break; default: done = true; break; } return(done); }
bool HandleTask(StoryTask task) { //#if UNITY_EDITOR && UNITY_IOS // // Pause applicatin simulation for debugging. // if (Input.GetKeyDown("p")) // { // OnApplicationPause(true); // } // if (Input.GetKeyDown("o")) // { // OnApplicationPause(false); // } // if (state == STATE.PAUSED) // return; //#endif //if (displayNetworkGUIState()) // SetNetworkIndicators(); // Task needs to be executed. // first check for storyengine tasks. bool done = false; switch (task.Instruction) { // ---------------------------- SCOPE ---------------------------- case "isglobal": if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL) { task.Pointer.scope = SCOPE.GLOBAL; Log("Setting pointer scope to global: " + task.Pointer.currentPoint.StoryLine); } else { Log("No global authority, not changing pointer scope. "); } done = true; break; case "islocal": task.Pointer.SetLocal(); Log("Setting pointer scope to local: " + task.Pointer.currentPoint.StoryLine); done = true; break; case "setglobalauth": case "amhub": GENERAL.AUTHORITY = AUTHORITY.GLOBAL; Log("Setting authority to global"); // AlertHandler.Log("Launching environment " + SessionSettings.Environment); // Log("Launching " + Management.UserSettingsHandler.GetActiveModuleName() + ", environment " + StorageSettings.Environment); done = true; break; case "setlocalauth": case "amremote": GENERAL.AUTHORITY = AUTHORITY.LOCAL; Log("Setting authority to local"); // AlertHandler.Log("Launching environment " + SessionSettings.Environment); // Log("Launching " + Management.UserSettingsHandler.GetActiveModuleName() + ", environment " + StorageSettings.Environment); done = true; break; // ---------------------------- SERVER SIDE ---------------------------- case "startserver": // Start broadcast and network server if not already going. if (!Broadcasting) { if (BroadcastKey == 0 || BroadcastMessageString == null || BroadcastMessageString.Length == 0) { // no info on how to configure broadcasterver Warning("No broadcast info, not starting broadcastserver."); } else { startBroadcastServer(BroadcastKey, BroadcastMessageString); Log("Starting broadcast server, key: " + BroadcastKey + " message: " + BroadcastMessageString); } //ModuleInfo mi = SessionData.GetModuleInfo(); //if (mi != null) //{ // startBroadcastServer(mi.BroadcastKey, mi.ID); // Log("Starting broadcast server, key: " + mi.BroadcastKey + " message: " + mi.ID); //} //else //{ // Warning("No module info, starting generic broadcast server."); // Log("key: " + ConnectionKey + " message: " + ConnectionMessage); // startBroadcastServer(ConnectionKey, ConnectionMessage); //} } if (!Serving) { Log("Starting network server."); startNetworkServer(); } done = true; break; case "stopserver": // Stop broadcast and network server Log("Stopping broadcast server."); stopBroadcast(); Log("Stopping network server."); stopNetworkServer(); done = true; break; case "monitorconnections": if (NetworkObject == null || state == STATE.PAUSED) { break; } // Keep servers up on IOS (because of application pause) #if UNITY_IOS if (!Broadcasting) { Log("Starting broadcast server, key " + BroadcastKey + " message " + BroadcastMessageString); //startBroadcastServer(ConnectionKey, ConnectionMessage); startBroadcastServer(BroadcastKey, BroadcastMessageString); } if (!Serving) { Log("Starting network server."); startNetworkServer(); } #endif // Watch for new clients. Stays live indefinitely. if (Serving && NewTrackedAddresses()) { // new addresses connected since last call foreach (string add in NewConnectedAddresses()) { Log("New client at " + add); // note: pass addresses on so we can send targeted messages... } task.setCallBack("addclient"); } if (networkManager != null) { NetworkConnection[] connections = networkManager.GetConnectedClients(); task.SetStringValue("debug", "clients: " + connections.Length); WasConnected = connections.Length > 0; } else { Warning("Network manager not available, cannot monitor connections"); } //task.SetStringValue("debug", "clients: " + TrackedConnectedAddresses().Count); //WasConnected = TrackedConnectedAddresses().Count > 0; break; //case "pushglobaldata": // done = true; // break; case "pushglobaltasks": // go over all pointers and mark everything as modified // that way it'll get sent. // with single client apps this is ok, but for multiple clients we need something more advanced Log("Pushing global tasks."); foreach (StoryTask theTask in GENERAL.ALLTASKS) { if (theTask.scope == SCOPE.GLOBAL) { theTask.MarkAllAsModified(); } } done = true; break; // ---------------------------- CLIENT SIDE ---------------------------- case "serversearch": if (NetworkObject == null || state == STATE.PAUSED) { break; } if (Listening) { if (foundServer()) { stopBroadcast(); string message = networkBroadcast.serverMessage; char[] param = { '\0' }; string[] split = message.Split(param); task.SetStringValue("persistantData", split[0]); task.setCallBack("serverfound"); Listening = false; Log("Found broadcast server: " + networkBroadcast.serverAddress + " key: " + networkBroadcast.broadcastKey + " message: " + split[0]); done = true; } } else { /* * ModuleInfo mi = SessionData.GetModuleInfo(); * if (mi != null) * { * startBroadcastClient(mi.BroadcastKey); * Log("(Re)starting broadcast listening for key " + mi.BroadcastKey); * } * else * { * startBroadcastClient(BroadcastKey); * Log("(Re)starting broadcast listening for key " + BroadcastKey); * } */ startBroadcastClient(BroadcastKey); Log("(Re)starting broadcast listening for key " + BroadcastKey); Listening = true; } break; case "startclient": string ServerAddress = networkBroadcast.serverAddress; if (ServerAddress != "") { // Log("Starting network client for remote server " + ServerAddress); startNetworkClient(ServerAddress); } else { Warning("Trying to start client without server address."); } done = true; break; case "stopclient": // Verbose("Stopping network client."); StopNetworkClient(); WasConnected = false; done = true; break; case "monitorserver": // Watches the connection, stays active until connection lost. if (clientIsConnected()) { WasConnected = true; } else { if (WasConnected) { // Lost connection. Restart server search. Log("Lost server connection."); StopNetworkClient(); WasConnected = false; task.setCallBack("client"); done = true; } else { // } } break; default: done = true; break; } return(done); }
/* * private async void LoadScript() * { * if (scriptAddressable != null && scriptAddressable != "") * { * theDirector.status = DIRECTORSTATUS.PAUSED; * AsyncOperationHandle<TextAsset> handle = Addressables.LoadAssetAsync<TextAsset>(scriptAddressable); * await handle.Task; * * if (handle.Status==) * if (handle.Result == null) * { * Error("Addressable script asset failed: " + obj.OperationException.Message); * * } * else * { * * theDirector.loadScriptAsset(obj.Result); * Log("Script loaded, from addressable asset."); * * * } * * } * * * * * * // The task is complete. Be sure to check the Status is successful before storing the Result. * * } * */ #region SENDUPDATES void LateUpdate() { // Check if any data changed during this frame, and send updates across network. StoryUpdate storyUpdate = new StoryUpdate(); // Contains a collection of task and pointer updates. // Is there a networkhandler. if (NetworkHandler.Instance != null) { int Queue = NetworkHandler.Instance.GetQueueSize(); if (Queue == -1) { // Verbose("no network active"); } else { switch (Queue) { case 0: BufferStatusOut = 0; break; case 1: case 2: BufferStatusOut = 1; break; default: BufferStatusOut = 2; break; } // forcing always send. // this means that a storyupdate is sent for every frame and they all arrive in order. // they get executed in order as well, but they may be batched together. // so multiple tasks might get executed in a single frame. they will be executed in the correct order. if (Queue < 3 || true) { // Iterate over all pointers to see if any were killed. Clients do not kill pointers themselves. // For consistency of network logic, local pointers that were killed are disposed by the director. // Global pointers are disposed here, after updating clients about them. for (int p = GENERAL.ALLPOINTERS.Count - 1; p >= 0; p--) { StoryPointer pointer = GENERAL.ALLPOINTERS[p]; if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL && pointer.scope == SCOPE.GLOBAL && pointer.GetStatus() == POINTERSTATUS.KILLED) //if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL && pointer.scope == SCOPE.GLOBAL && pointer.modified && pointer.GetStatus() == POINTERSTATUS.KILLED) { Log("Sending pointer kill update to clients: " + pointer.currentPoint.StoryLine); storyUpdate.AddStoryPointerUpdate(pointer.GetUpdate()); // bundled //pointer.modified = false; Log("Removing pointer " + pointer.currentPoint.StoryLine); GENERAL.ALLPOINTERS.Remove(pointer); } } // Iterate over all tasks. for (int i = GENERAL.ALLTASKS.Count - 1; i >= 0; i--) { StoryTask task = GENERAL.ALLTASKS[i]; // Cleanup completed tasks. if (task.getStatus() == TASKSTATUS.COMPLETE) { GENERAL.ALLTASKS.RemoveAt(i); Verbose("Task " + task.Instruction + " on storyline " + task.Pointer.currentPoint.StoryLine + " completed, removed from alltasks. "); } if (task.modified) { // Debugging: if a pointer is in the process of being killed, we may want to not send task updates // as they might result in the task being recreated clientside. if (task.Pointer.GetStatus() == POINTERSTATUS.KILLED) { Warning("Supressing sending task update for task with pointer that is dying. " + task.Instruction); } else { // Check if we need to send network updates. switch (GENERAL.AUTHORITY) { case AUTHORITY.LOCAL: if (task.scope == SCOPE.GLOBAL) { Verbose("Global task " + task.Instruction + " with id " + task.PointID + " changed, adding to update for server."); storyUpdate.AddTaskUpdate(task.GetUpdate()); // bundled } break; case AUTHORITY.GLOBAL: if (task.scope == SCOPE.GLOBAL) { Verbose("Global task " + task.Instruction + " with id " + task.PointID + " changed, adding to update for clients."); storyUpdate.AddTaskUpdate(task.GetUpdate()); // bundled } break; default: break; } task.modified = false; } } } // If anything to send, send. if (storyUpdate.AnythingToSend()) { switch (GENERAL.AUTHORITY) { case AUTHORITY.LOCAL: if (NetworkHandler.Instance != null) { NetworkHandler.Instance.SendStoryUpdateToServer(storyUpdate); } Log("Sending story update to server. \n" + storyUpdate.DebugLog); break; case AUTHORITY.GLOBAL: if (NetworkHandler.Instance != null) { NetworkHandler.Instance.SendStoryUpdateToClients(storyUpdate); } Log("Sending story update to clients. \n" + storyUpdate.DebugLog); //Debug.Log(storyUpdate.ToString()); break; default: break; } } } } /* * * * * if (NetworkHandler.Instance.IsClient()) * { * StoryUpdate storyUpdate = new StoryUpdate(); // Contains a collection of dataupdates. * * // Iterate over all data. * * for (int i = GENERAL.ALLTASKS.Count - 1; i >= 0; i--) * { * StoryTask data = GENERAL.ALLTASKS[i]; * * if (data.modified) * { * // Data was modified locally, if global, compile an update for the server. * * if (data.scope == SCOPE.GLOBAL) * { * Verbose("Data was modified locally as client, creating update for server for " + data.ID); * storyUpdate.AddDataUpdate(data.GetDataUpdate()); // bundled * } * * data.modified = false; * } * } * * // If anything to send, send. * * if (storyUpdate.AnythingToSend()) * * { * * NetworkHandler.Instance.SendStoryUpdateToServer(storyUpdate); * * Verbose("Sending story update to server. \n" + storyUpdate.DebugLog); * * * * } * * * * } * * * * * // If server, messages may differ for clients. * * * if (NetworkHandler.Instance.IsServer()) * { * * string[] clients = NetworkHandler.Instance.TrackedConnectedAddresses().ToArray(); * * for (int c = 0; c < clients.Length; c++) * { * * StoryUpdate storyUpdate = new StoryUpdate(); // Contains a collection of dataupdates. * * // Iterate over all data. * * for (int i = GENERAL.ALLTASKS.Count - 1; i >= 0; i--) * { * StoryTask data = GENERAL.ALLTASKS[i]; * * if (data.modified) * { * // Data was modified locally, if global, compile an update for the server. * * if (data.scope == SCOPE.GLOBAL) * { * Verbose("Data was modified locally as client, creating update for server for " + data.ID); * storyUpdate.AddDataUpdate(data.GetDataUpdateFor(clients[c])); // bundled * } * * data.modified = false; * } * } * * // If anything to send, send. * * if (storyUpdate.AnythingToSend()) * * { * NetworkHandler.Instance.SendStoryUpdateToClient(storyUpdate, clients[c]); * * Verbose("Sending story update to client. \n" + storyUpdate.DebugLog); * * * * } * * * } * * * * * } * * */ } }
void Update() { SetDebugLevels(); #region APPLYUPDATES if (NetworkHandler.Instance != null) { // Handle story updates, aiming for 1 per frame, assuming we're tyring to run in sync. // Lowest numbers are oldest. int UpdateCount = NetworkHandler.Instance.StoryUpdateStack.Count; switch (UpdateCount) { case 0: BufferStatusIn = 0; break; case 1: // exactly one, so apply. ApplyStoryUpdate(NetworkHandler.Instance.StoryUpdateStack[0]); NetworkHandler.Instance.StoryUpdateStack.RemoveAt(0); BufferStatusIn = 1; break; case 2: // Two, normally for different frames that happened to arrive in the same frame on this end. // Apply the oldest one, keep the other because we expect to get 0 updates during our next frame // (If we're on similar fps) ApplyStoryUpdate(NetworkHandler.Instance.StoryUpdateStack[0]); NetworkHandler.Instance.StoryUpdateStack.RemoveAt(0); BufferStatusIn = 1; break; default: // More than 2. Apply all the older ones in order of arrival, keep latest one because we may get 0 next frame. Warning("Update buffer >2"); BufferStatusIn = 2; while (NetworkHandler.Instance.StoryUpdateStack.Count > 1) { ApplyStoryUpdate(NetworkHandler.Instance.StoryUpdateStack[0]); NetworkHandler.Instance.StoryUpdateStack.RemoveAt(0); } break; } } #endregion #region DIRECTOR switch (theDirector.status) { case DIRECTORSTATUS.ACTIVE: // Verbose("Director active ."); foreach (StoryTask task in GENERAL.ALLTASKS) { if (task.getCallBack() != "") { // if a callback was set (somewhere on the network) we act on it only if we are the server or if the task is local. if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL || task.scope == SCOPE.LOCAL) // if (true || task.scope == SCOPE.LOCAL) { task.Pointer.SetStatus(POINTERSTATUS.TASKUPDATED); } } } theDirector.EvaluatePointers(); List <StoryTask> newTasks = new List <StoryTask>(); for (int p = 0; p < GENERAL.ALLPOINTERS.Count; p++) { StoryPointer pointer = GENERAL.ALLPOINTERS[p]; switch (pointer.scope) { case SCOPE.GLOBAL: // If pointer scope is global, we add a task if our own scope is global as well. (If our scope is local, we'll be receiving the task over the network) if (GENERAL.AUTHORITY == AUTHORITY.GLOBAL) // if (true) { if (pointer.GetStatus() == POINTERSTATUS.NEWTASK) { pointer.SetStatus(POINTERSTATUS.PAUSED); StoryTask task = pointer.SpawnTask(); // StoryTask task = new StoryTask(pointer, SCOPE.GLOBAL); task.LoadPersistantData(pointer); newTasks.Add(task); task.modified = true; Verbose("Created global task " + task.Instruction + " with pointid " + task.PointID + " for pointer " + pointer.currentPoint.StoryLine); } } break; case SCOPE.LOCAL: default: // If pointer scope is local, check if new tasks have to be generated. if (pointer.GetStatus() == POINTERSTATUS.NEWTASK) { pointer.SetStatus(POINTERSTATUS.PAUSED); StoryTask task = pointer.SpawnTask(); //StoryTask task = new StoryTask(pointer, SCOPE.LOCAL); task.LoadPersistantData(pointer); newTasks.Add(task); Verbose("Created local task " + task.Instruction + " with pointid " + task.PointID + " for pointer " + pointer.currentPoint.StoryLine); } break; } } if (newTasks.Count > 0) { if (newTasksEventUnity != null) { newTasksEventUnity.Invoke(newTasks); } // if (newTasksEvent != null) newTasksEvent(this, new TaskArgs(newTasks)); // trigger the event, if there are any listeners // DistributeTasks(new TaskArgs(newTasks)); // if any new tasks call an event, passing on the list of tasks to any handlers listening } break; case DIRECTORSTATUS.READY: int SIGNOFFS = eventHandlerCount(); if (SIGNOFFS == 0) { Error("No handlers registred. Pausing director."); theDirector.status = DIRECTORSTATUS.PAUSED; } else { Verbose("" + SIGNOFFS + " handlers registred at this time."); Verbose("Starting storyline " + launchStoryline); theDirector.NewStoryLine(launchStoryline); theDirector.status = DIRECTORSTATUS.ACTIVE; } break; case DIRECTORSTATUS.NOTREADY: // try addressable script first. //if (scriptAddressable != null && scriptAddressable != "") //{ // // pause while loading // theDirector.status = DIRECTORSTATUS.PAUSED; // Addressables.LoadAssetAsync<TextAsset>(scriptAddressable).Completed += obj => // { // if (obj.Result == null) // { // Error("Addressable script asset failed: " + obj.OperationException.Message); // } // else // { // theDirector.loadScriptAsset(obj.Result); // Log("Script loaded, from addressable asset."); // } // }; //} if (scriptFile != null && scriptFile != "") { // we have a reference to a local script file, so we'll attempt to load it (synchronous). we try to load it from a folder with the current version string script = Transport.FileToText(Application.persistentDataPath + "/scripts/" + App.GetMinor() + "/" + scriptFile); if (script == "") { Log("No script on disk for version: " + App.GetMinor()); } else { theDirector.loadScriptText(script); Log("Script loaded, from local file."); break; } } if (scriptAsset != null) { theDirector.loadScriptAsset(scriptAsset); Log("Script loaded, from asset."); break; } Error("No script reference found, pausing director."); theDirector.status = DIRECTORSTATUS.PAUSED; break; default: break; } #endregion }