/** * Adds a command to be processed. The service guarantees that * all added commands will be processed and any response handler will * always be called regardless of if previous commands experience * errors. Furthermore added commands will always be executed in the * order they were added. * <p> * Note that adding commands using this method is equivalent to * registering a process commands callback and adding commands * when the process commands callback is made. This means that any * callbacks already registered will be executed before the command * (or commands if the delayProcessing flag is used) added using this * method. * <p> * Example: Adding commands A, B, and C with delayProcessing set to * true for A and B, but false for C will be equivalent to register a * callback and add A, B, and C when the callback is made. * * @param command The command to add. * * @param handler Optional. If specified, this is * a callback that will be called when the command has been * processed. The object passed in the callback can be used to check * if the command succeeded and to access any returned data. * * @param stateData Optional. The state data to use. If null or omitted * the default state data will be used as specified in the constructor. * * @param delayProcessing Optional. A hint that tells the service not to try to send the * command immediately. This hint is useful when adding a sequence * of commands in one go. Specifying this flag to true for all * commands except the last one added will ensure that the Service * don't start processing the events immediately, but holds * processing until the last command in the sequence has been added. **/ public void AddCommand(IRSCommand command, ResponseHandler handler, RSStateData stateData = null, bool delayProcessing = false) { if (stateData == null) { stateData = DefaultStateData; } lock (callbackQueue) { if (currentServiceCallback == null) { currentServiceCallback = new RSCommandSequence(this, stateData); } else if (currentServiceCallback.StateData != stateData) { AddCommandSequence(currentServiceCallback); currentServiceCallback = new RSCommandSequence(this, stateData); } currentServiceCallback.AddCommand(command, handler); if (!delayProcessing) { AddCommandSequence(currentServiceCallback); currentServiceCallback = null; } } }
public void AddToSequence(RSCommandSequence seq) { foreach (RSOutgoingCommand cmd in Commands) { seq.AddCommand(cmd.Command, cmd.Callback); } }
/** * Copies the contents from the argument into this sequence. * Note that anything in this sequence will be overwritten. */ public void CopyFrom(RSCommandSequence seq) { Service = seq.Service; commands = seq.commands; ContainsResponseHandlers = seq.ContainsResponseHandlers; ContainsRenderCommands = seq.ContainsRenderCommands; StateData = seq.StateData; }
/** * Function that process all the commands in the given * RSCommandSequence. An assumption is made that all the commands are safe * to send in a single request. If there is a render command, it must be * the last command, and no other commands may have callbacks. */ protected void ProcessCommands(RSCommandSequence seq) { lock (callbackQueue) { // If no commands left to process, flag that we are no longer busy // and continue to process callbacks. if (seq.Commands.Count == 0) { isBusy = false; ProcessCallbacks(); return; } // Array containing the commands to send. IList <RSOutgoingCommand> commands = null; IList <IRSCommand> stateCommands = null; if (seq.StateData != null && seq.StateData.StateCommands != null) { stateCommands = seq.StateData.StateCommands; } // Build the array of commands to send. if (stateCommands == null || stateCommands.Count == 0) { // No state commands, use the seq.Commands directly. commands = seq.Commands; } else { // Build a new array including the state commands with all the commands. commands = new List <RSOutgoingCommand>(); // Add state commands. if (stateCommands != null) { foreach (IRSCommand cmd in stateCommands) { commands.Add(new RSOutgoingCommand(this, cmd)); } } // Add normal commands. foreach (RSOutgoingCommand cmd in seq.Commands) { commands.Add(cmd); } } currentCommands = commands; currentCommandsResponseMap = new Dictionary <int, RSOutgoingCommand>(); StringBuilder arrStr = new StringBuilder(); bool first = true; arrStr.Append('['); foreach (RSOutgoingCommand cmd in commands) { // Add the command to the response map if it has a callaback. if (cmd.CommandId >= 0) { currentCommandsResponseMap[cmd.CommandId] = cmd; } if (!first) { arrStr.Append(','); } first = false; try { arrStr.Append(cmd.ToJSON()); } catch (Exception e) { // FIXME: // Strictly speaking, this callback can't be made // immediately since that breaks the service contract that // response handlers are called in the same order they are // handed to the service. // failed to json serialize the command. call response // handler with an error (if one is registered). cmd.DoClientErrorCallback("Failed to JSON serialize the command. " + e.ToString(), CLIENT_SIDE_ERROR_CODE_INTERNAL); } } arrStr.Append(']'); string url = BaseURL; if (seq.ContainsRenderCommands) { string uri = url + "?json_rpc_request=" + arrStr.ToString(); UnityWebRequest webRequest = new UnityWebRequest(uri, UnityWebRequest.kHttpVerbGET); webRequest.SetRequestHeader("Content-Type", "application/json"); webRequest.downloadHandler = new RSDownloadHandlerRender(this, webRequest); m_requests.Add(new RSSeriveRequest(webRequest)); webRequest.SendWebRequest(); } else { UnityWebRequest webRequest = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST); webRequest.SetRequestHeader("Content-Type", "application/json"); webRequest.downloadHandler = new RSDownloadHandler(this, webRequest); webRequest.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(arrStr.ToString())); m_requests.Add(new RSSeriveRequest(webRequest)); webRequest.SendWebRequest(); } } }
/** * Processes the callbacks in the callback queue. */ protected void ProcessCallbacks() { // If we are busy then nothing to do at the moment. RSCommandSequence safeSeq = null; lock (callbackQueue) { if (isBusy) { return; } // Check if we have left-over work to do. if (currentCmdSequence != null) { // Indicate that we are busy. This prevents any process_callbacks calls // generated by the callbacks to be processed immediately. isBusy = true; // We need to split up the stored command sequence into chunks // that are safe to send. safeSeq = currentCmdSequence.GetSafeSubsequence(); // If no more commands left in m_current_cmd_sequence, set it to // null. Otherwise keep it and process it again next call. if (currentCmdSequence.Commands.Count == 0) { currentCmdSequence = null; } ProcessCommands(safeSeq); return; } // If no callbacks in the queue, then we are done for now. if (callbackQueue.Count == 0) { return; } // Indicate that we are busy. isBusy = true; // The command sequence we need to fill with commands from callbacks. RSCommandSequence seq = null; // Go through all the current callbacks (and any callbacks // added by those callbacks) until we hit a binary command // or a callback using a different stateData instance. int callbackCount = 0; while (callbackQueue.Count > 0) { CallbackWrapper frontCallback = callbackQueue.Peek(); // Create the command sequence if not done yet. if (seq == null) { seq = new RSCommandSequence(this, frontCallback.StateData); } // Check if the next callback is using the same StateData. // If not we need to stop and process the current command // sequence first. if (frontCallback.StateData != seq.StateData) { break; } // Next callback is using the same state data, so call it // and add the commands to seq. frontCallback = callbackQueue.Dequeue(); try { frontCallback.Callback(seq); } catch (Exception e) { // Error Logger.Log("error", "Error in callback: " + frontCallback + "\n- " + e.ToString()); } callbackCount++; // If we get a sequence containing binary commands we // need to stop, otherwise continue calling callbacks if // there are any left. if (seq.ContainsRenderCommands) { break; } } // We need to split up the command sequence into chunks that are safe to send. safeSeq = seq.GetSafeSubsequence(); // If there are still commands in the sequence, then we need // to store it and continue processing it later. if (seq.Commands.Count > 0) { currentCmdSequence = seq; } } ProcessCommands(safeSeq); }
/** * Helper function for adding already made command sequences to the current queue. */ public void AddCommandSequence(RSCommandSequence seq, bool delayProcessing = false) { AddCallback(seq.AddToSequence, seq.StateData, delayProcessing); }
/** * Function that process all the commands in the given * RSCommandSequence. An assumption is made that all the commands are safe * to send in a single request. If there is a render command, it must be * the last command, and no other commands may have callbacks. */ protected void ProcessCommands(RSCommandSequence seq) { lock (callbackQueue) { // If no commands left to process, flag that we are no longer busy // and continue to process callbacks. if (seq.Commands.Count == 0) { isBusy = false; ProcessCallbacks(); return; } // Array containing the commands to send. IList <RSOutgoingCommand> commands = null; IList <IRSCommand> stateCommands = null; if (seq.StateData != null && seq.StateData.StateCommands != null) { stateCommands = seq.StateData.StateCommands; } // Build the array of commands to send. if (stateCommands == null || stateCommands.Count == 0) { // No state commands, use the seq.Commands directly. commands = seq.Commands; } else { // Build a new array including the state commands with all the commands. commands = new List <RSOutgoingCommand>(); // Add state commands. if (stateCommands != null) { foreach (IRSCommand cmd in stateCommands) { commands.Add(new RSOutgoingCommand(this, cmd)); } } // Add normal commands. foreach (RSOutgoingCommand cmd in seq.Commands) { commands.Add(cmd); } } currentCommands = commands; currentCommandsResponseMap = new Dictionary <int, RSOutgoingCommand>(); StringBuilder arrStr = new StringBuilder(); bool first = true; arrStr.Append('['); foreach (RSOutgoingCommand cmd in commands) { // Add the command to the response map if it has a callaback. if (cmd.CommandId >= 0) { currentCommandsResponseMap[cmd.CommandId] = cmd; } if (!first) { arrStr.Append(','); } first = false; try { arrStr.Append(cmd.ToJSON()); } catch (Exception e) { // FIXME: // Strictly speaking, this callback can't be made // immediately since that breaks the service contract that // response handlers are called in the same order they are // handed to the service. // failed to json serialize the command. call response // handler with an error (if one is registered). cmd.DoClientErrorCallback("Failed to JSON serialize the command. " + e.ToString(), CLIENT_SIDE_ERROR_CODE_INTERNAL); } } arrStr.Append(']'); string url = BaseURL; if (seq.ContainsRenderCommands) { // Do render! using (RSWebClient webClient = new RSWebClient(Timeout)) { webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(RenderCompleted); webClient.Headers.Add("Content-Type", "application/json"); Uri uri = new Uri(url + "?json_rpc_request=" + arrStr.ToString()); Logger.Log("debug", "Getting render: " + uri.ToString()); webClient.DownloadDataAsync(uri); } } else { using (RSWebClient webClient = new RSWebClient(Timeout)) { webClient.UploadStringCompleted += new UploadStringCompletedEventHandler(NonRenderCompleted); webClient.Headers.Add("Content-Type", "application/json"); Logger.Log("debug", "Sending to: " + url + " | " + arrStr.ToString()); webClient.UploadStringAsync(new Uri(url), arrStr.ToString()); } } } }
protected void OnAppIniting(RSCommandSequence seq) { UpdateIrayBackground(seq); }
/** * Returns a sub sequence as an RSCommandSequence object that is safe to send in a single HTTP request. */ public RSCommandSequence GetSafeSubsequence() { RSCommandSequence safeSeq = new RSCommandSequence(Service, StateData); if (commands.Count == 0) { return safeSeq; } // Does the subsequence contain callbacks. bool hasCallbacks = false; // Does the subsequence contain a render. bool hasRenderCommand = false; // Go through commands an decide which are safe in a single request int i = 0; for (; i < commands.Count; i++) { RSOutgoingCommand command = commands[i]; if (command.Command is RSRenderCommand) { // Two cases: if (hasCallbacks) { // Case 1: There are callbacks, // so not safe to include the render command. break; } // Case 2: There are no callbacks, so we can add the render // command to the safe sequence and then break. hasRenderCommand = true; if (command.Callback != null) { hasCallbacks = true; } // We must increase i otherwise the render command will not be included. i++; break; } if (command.Callback != null) { hasCallbacks = true; } } // i now equals the number of commands that are safe to send. Splice the // array into a safe part (return) and a non-safe part (keep). List<RSOutgoingCommand> safeCommands = commands; if (i < commands.Count) { safeCommands = commands.GetRange(0, i); commands.RemoveRange(0, i); } else { commands = new List<RSOutgoingCommand>(); } safeSeq.commands = safeCommands; safeSeq.ContainsResponseHandlers = hasCallbacks; safeSeq.ContainsRenderCommands = hasRenderCommand; return safeSeq; }
protected void OnAppIniting(RSCommandSequence seq) { if (Moved == true) { UpdateMovement(seq); } }
/** * Returns a sub sequence as an RSCommandSequence object that is safe to send in a single HTTP request. */ public RSCommandSequence GetSafeSubsequence() { RSCommandSequence safeSeq = new RSCommandSequence(Service, StateData); if (commands.Count == 0) { return(safeSeq); } // Does the subsequence contain callbacks. bool hasCallbacks = false; // Does the subsequence contain a render. bool hasRenderCommand = false; // Go through commands an decide which are safe in a single request int i = 0; for (; i < commands.Count; i++) { RSOutgoingCommand command = commands[i]; if (command.Command is RSRenderCommand) { // Two cases: if (hasCallbacks) { // Case 1: There are callbacks, // so not safe to include the render command. break; } // Case 2: There are no callbacks, so we can add the render // command to the safe sequence and then break. hasRenderCommand = true; if (command.Callback != null) { hasCallbacks = true; } // We must increase i otherwise the render command will not be included. i++; break; } if (command.Callback != null) { hasCallbacks = true; } } // i now equals the number of commands that are safe to send. Splice the // array into a safe part (return) and a non-safe part (keep). List <RSOutgoingCommand> safeCommands = commands; if (i < commands.Count) { safeCommands = commands.GetRange(0, i); commands.RemoveRange(0, i); } else { commands = new List <RSOutgoingCommand>(); } safeSeq.commands = safeCommands; safeSeq.ContainsResponseHandlers = hasCallbacks; safeSeq.ContainsRenderCommands = hasRenderCommand; return(safeSeq); }
/** * Function that process all the commands in the given * RSCommandSequence. An assumption is made that all the commands are safe * to send in a single request. If there is a render command, it must be * the last command, and no other commands may have callbacks. */ protected void ProcessCommands(RSCommandSequence seq) { lock (callbackQueue) { // If no commands left to process, flag that we are no longer busy // and continue to process callbacks. if (seq.Commands.Count == 0) { isBusy = false; ProcessCallbacks(); return; } // Array containing the commands to send. IList<RSOutgoingCommand> commands = null; IList<IRSCommand> stateCommands = null; if (seq.StateData != null && seq.StateData.StateCommands != null) { stateCommands = seq.StateData.StateCommands; } // Build the array of commands to send. if (stateCommands == null || stateCommands.Count == 0) { // No state commands, use the seq.Commands directly. commands = seq.Commands; } else { // Build a new array including the state commands with all the commands. commands = new List<RSOutgoingCommand>(); // Add state commands. if (stateCommands != null) { foreach (IRSCommand cmd in stateCommands) { commands.Add(new RSOutgoingCommand(this, cmd)); } } // Add normal commands. foreach (RSOutgoingCommand cmd in seq.Commands) { commands.Add(cmd); } } currentCommands = commands; currentCommandsResponseMap = new Dictionary<int, RSOutgoingCommand>(); StringBuilder arrStr = new StringBuilder(); bool first = true; arrStr.Append('['); foreach (RSOutgoingCommand cmd in commands) { // Add the command to the response map if it has a callaback. if (cmd.CommandId >= 0) { currentCommandsResponseMap[cmd.CommandId] = cmd; } if (!first) { arrStr.Append(','); } first = false; try { arrStr.Append(cmd.ToJSON()); } catch (Exception e) { // FIXME: // Strictly speaking, this callback can't be made // immediately since that breaks the service contract that // response handlers are called in the same order they are // handed to the service. // failed to json serialize the command. call response // handler with an error (if one is registered). cmd.DoClientErrorCallback("Failed to JSON serialize the command. " + e.ToString(), CLIENT_SIDE_ERROR_CODE_INTERNAL); } } arrStr.Append(']'); string url = BaseURL; if (seq.ContainsRenderCommands) { // Do render! using (RSWebClient webClient = new RSWebClient(Timeout)) { webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(RenderCompleted); webClient.Headers.Add("Content-Type", "application/json"); Uri uri = new Uri(url + "?json_rpc_request=" + arrStr.ToString()); Logger.Log("debug", "Getting render: " + uri.ToString()); webClient.DownloadDataAsync(uri); } } else { using (RSWebClient webClient = new RSWebClient(Timeout)) { webClient.UploadStringCompleted += new UploadStringCompletedEventHandler(NonRenderCompleted); webClient.Headers.Add("Content-Type", "application/json"); Logger.Log("debug", "Sending to: " + url + " | " + arrStr.ToString()); webClient.UploadStringAsync(new Uri(url), arrStr.ToString()); } } } }
protected void OnAppInitingCallback(RSCommandSequence seq) { if (OnAppIniting != null) { OnAppIniting(seq); } }
protected void OnAppIniting(RSCommandSequence seq) { UpdateGroundShadows(seq); }