From 56bf36ecc0b5da31250a0d10826ec4c58d83a142 Mon Sep 17 00:00:00 2001 From: Lelebees Date: Wed, 1 Dec 2021 13:45:46 +0100 Subject: [PATCH] Committing V1.2 --- TrackOS.cs | 230 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 155 insertions(+), 75 deletions(-) diff --git a/TrackOS.cs b/TrackOS.cs index 6641640..0d259e0 100644 --- a/TrackOS.cs +++ b/TrackOS.cs @@ -1,122 +1,195 @@ -//Script made by Lelebees: Last update at 10-01-2021 18:44 UTC+1 (Central European Time) +//Script made by Lelebees: Last update at 18-10-2021 10:15 UTC+2 (Central European Summer Time) +//TrackOS V1.2 (Not backwards compatible) //Tweakable variables below here - string antennaName = "Antenna"; string broadcastChannel = "channel 1"; //you can set this to literally anything, and I advise you to do so, to keep out people who are guessing lucky. bool receiverSystem = false; //set to true if you want to receive a programmable block's location. string outputPanelName = "Output LCD"; -int screenNumber = 0; +int screenNumber = 0; //this is the integer that use can use to decide which cockpit/PB screen (or whatever) you want to use. No guarantee this works on modded blocks! -bool policeSender = false; //receiver system only. Recommended false when using multiple Senders on one channel. +//reciever only variables +bool policeSender = true; //receiver system only. Recommended false when using multiple Senders on one channel. long allowedProgBlockID = 1234567890; //This is the ID of the programmable block you are receiving from. -string attachedTimerName = "Timer Block"; +//sender only variables long receiverProgBlockID = 1234567890; //This is the ID of the programmable block you are sending to. Use this to immedeately trigger a send when the Receiver is in range -bool checkingRange = true; +bool checkingRange = true; // The on off switch for the above mentioned feature. +bool useInternalTimer = true; //choose if you want the internal timer to work, or if you want to use your own timers +int timeDelay = 1800; //delay between send commands in ticks (60 ticks = 1 second) (Send Systems only) -//Do NOT touch the variables below this line unless you know what you are doing! This WILL break the program. -bool setup = false; -IMyRadioAntenna antenna; -int messageCount = 0; +//Do NOT touch the variables below this line. This WILL break the program. bool sentInrangeMessage = false; +int messageCount = 0; +int tickCount = 0; +IMyRadioAntenna antenna; IMyTerminalBlock statusDisplay; IMyTextSurface textSurface; -IMyTimerBlock timer; +IEnumerator _stateMachine; + +public Program() //This is the setup, which is automatically ran every time script is compiled or after a world reload +{ + Runtime.UpdateFrequency = UpdateFrequency.Update1; + //assign blocks to variables + antenna = GridTerminalSystem.GetBlockWithName(antennaName) as IMyRadioAntenna; + statusDisplay = GridTerminalSystem.GetBlockWithName(outputPanelName); + //Decide what type of TextSurface the display is. + if (statusDisplay is IMyTextSurface) + { + textSurface = (IMyTextSurface)statusDisplay; + } + else if (statusDisplay is IMyTextSurfaceProvider) + { + textSurface = ((IMyTextSurfaceProvider)statusDisplay).GetSurface(screenNumber); + } + else + { + textSurface = ((IMyTextSurfaceProvider)Me).GetSurface(screenNumber); + } + Echo("Setup complete. System messages will now appear on displays.\n"); + textSurface.WriteText("Setup complete.\n", false); + + if (receiverSystem == false) + { + _stateMachine = Send(broadcastChannel); + Runtime.UpdateFrequency |= UpdateFrequency.Once; + Echo ("Sending test complete. System should be ready to send coordinates."); + } +} -public void Main(string arg) +public void Main(string arg, UpdateType updateType) { - //Make the script autorun - Runtime.UpdateFrequency = UpdateFrequency.Update100; - //Check if we've ran the setup - if (!setup) + // Usually I verify that the argument is empty or a predefined value before running the state + // machine. This way we can use arguments to control the script without disturbing the + // state machine and its timing. For the purpose of this example however, I will omit this. + + // We only want to run the state machine(s) when the update type includes the + // "Once" flag, to avoid running it more often than it should. It shouldn't run + // on any other trigger. This way we can combine state machine running with + // other kinds of execution, like tool bar commands, sensors or what have you. + if ((updateType & UpdateType.Once) == UpdateType.Once) { - Echo("Setup incomplete, running setup...\n"); - Setup(); - } //this if statement could probably be reversed but I'm too lazy + RunStateMachine(); + } else { + tickCount++; + arg = arg.ToLower(); switch(arg) - { // Here, we're taking a look at the input. Since it's case sensitive, we use the different "case" calls right under eachother, creating an or effect. + { case "send": - case "Send": - case "SEND": - //If we are supposed to send (will clean this up later) - antenna.EnableBroadcasting = true; - Send(Me.GetPosition().ToString(), broadcastChannel); - timer.StartCountdown(); + _stateMachine = Send(broadcastChannel); //this is where we set messageText + Runtime.UpdateFrequency |= UpdateFrequency.Once; + //RunStateMachine(); break; case "get id": - case "Get id": - case "GET id": - case "get ID": - case "Get ID": - case "GET ID": - //If player requests PB ID GetID(); break; default: - //If there is no argument given (keeps the program running) Loop(); break; } } } -public void Setup() +// ***MARKER: Coroutine Execution +public void RunStateMachine() { - //Assign blocks to variables - antenna = GridTerminalSystem.GetBlockWithName(antennaName) as IMyRadioAntenna; - timer = GridTerminalSystem.GetBlockWithName(attachedTimerName) as IMyTimerBlock; - statusDisplay = GridTerminalSystem.GetBlockWithName(outputPanelName); - //Decide what type of TextSurface the display is. - if (statusDisplay is IMyTextSurface) - { - textSurface = (IMyTextSurface)statusDisplay; - } - else if (statusDisplay is IMyTextSurfaceProvider) - { - textSurface = ((IMyTextSurfaceProvider)statusDisplay).GetSurface(screenNumber); - } - else + // If there is an active state machine, run its next instruction set. + if (_stateMachine != null) { - textSurface = ((IMyTextSurfaceProvider)Me).GetSurface(screenNumber); + // The MoveNext method is the most important part of this system. When you call + // MoveNext, your method is invoked until it hits a `yield return` statement. + // Once that happens, your method is halted and flow control returns _here_. + // At this point, MoveNext will return `true` since there's more code in your + // method to execute. Once your method reaches its end and there are no more + // yields, MoveNext will return false to signal that the method has completed. + // The actual return value of your yields are unimportant to the actual state + // machine. + bool hasMoreSteps = _stateMachine.MoveNext(); + + // If there are no more instructions, we stop and release the state machine. + if (hasMoreSteps) + { + // The state machine still has more work to do, so signal another run again, + // just like at the beginning. + Runtime.UpdateFrequency |= UpdateFrequency.Once; + } + else + { + _stateMachine.Dispose(); + + // In our case we just want to run this once, so we set the state machine + // variable to null. But if we wanted to continously run the same method, we + // could as well do + // _stateMachine = RunStuffOverTime(); + // instead. + _stateMachine = null; + } } - //Tell the player the setup was successfull by using both Echo and the textSurface - Echo("Setup complete. Further output will be displayed on the connected LCD\n"); - textSurface.WriteText("Setup complete.\n", false); - //Make sure we dont need to run the setup again - setup = true; } -public void Send(string messageText, string broadcastChannel) +// ***MARKER: Coroutine Example +// The return value (bool in this case) is not important for this example. It is not +// actually in use. +public IEnumerator Send(string broadcastChannel) { - IGC.SendBroadcastMessage(broadcastChannel, messageText, TransmissionDistance.TransmissionDistanceMax); - messageCount++; - textSurface.WriteText ("sent message ["+messageCount+"] succesfully.\n", true); - antenna.EnableBroadcasting = false; - //add custom actions the program must perform after sending a message here! + // Then we will tell the script to stop execution here and let the game do it's + // thing. The time until the code continues on the next line after this yield return + // depends on your State Machine Execution and the timer setup. + // The `true` portion is there simply because an enumerator needs to return a value + // per item, in our case the value simply has no meaning at all. You _could_ utilize + // it for a more advanced scheduler if you want, but that is beyond the scope of this + // tutorial. + + //pepare for sending + antenna.EnableBroadcasting = true; + messageCount++; + Vector3D Position = Me.GetPosition(); + string messageText = $"GPS:Tracker Location {messageCount}:{Position.X}:{Position.Y}:{Position.Z}:#FF0000:"; + Echo ("DEBUG A"); + + yield return true; //wait* a tick + yield return true; + + //Send the message + IGC.SendBroadcastMessage(broadcastChannel, messageText, TransmissionDistance.TransmissionDistanceMax); + textSurface.WriteText ("sent message ["+messageCount+"] succesfully.\n", true); + + //custom actions box (When something has been sent) + Echo("DEBUG B"); //end of custom actions box + + //prepare for ending the send thing + yield return true; //wait* a tick + yield return true; // wait another tick so this fucking thing actually sends + + // the following comment has been left in to illustrate my struggles with the program, it is no longer relevant + //okay so that didnt "Work" either. It does get to all this shite but no send. GO 9 YIELDS + + antenna.EnableBroadcasting = false; + Echo("DEBUG C"); + + // *The program doesn't actually wait. It stops and runs from that point on the next tick. this lowers the instruction count, making the script more preformance friendly, however, we're using it to make a delay in between certain actions + //IT WORKS, WHAHAHAH YES IT WORKS! FUCK YOU COROUTINES, I AM A GOD OF SCRIPTING! TREMBLE IN FEAR BEFORE THE GREAT AND MIGHTY LELEBEES! } -public void Receive(string broadcastChannel) +public void Receive(string broadcastChannel) //IGC magik { - //We use programming magic to create a listener, that will help us receive messages IGC.RegisterBroadcastListener(broadcastChannel); List listenerList = new List(); IGC.GetBroadcastListeners(listenerList); - if (listenerList[0].HasPendingMessage) //if there is a message that we haven't read yet: + if (listenerList[0].HasPendingMessage) //if there is a message in the queue { MyIGCMessage receivedMessage = new MyIGCMessage(); receivedMessage = listenerList[0].AcceptMessage(); - //"accept" the message and convert it to a readable format. - string receivedText = receivedMessage.Data.ToString(); - string receivedChannel = receivedMessage.Tag; - long messageSender = receivedMessage.Source; + string receivedText = receivedMessage.Data.ToString(); //take the message text + string receivedChannel = receivedMessage.Tag; //take the message tag (channel) + long messageSender = receivedMessage.Source; //take the sender ID //Do something with the information! if (policeSender == true) @@ -141,7 +214,7 @@ public void Receive(string broadcastChannel) { textSurface.WriteText("Message received on channel "+receivedChannel+"\n", true); textSurface.WriteText("Message content: "+receivedText+"\n", true); - //add custom actions here if policeSender is off and a message is received + //add custom actions here if policeSender is off //end of custom actions box } @@ -150,7 +223,6 @@ public void Receive(string broadcastChannel) public void GetID() { - //gets the current programmable block's ID and pastes it into the Custom Data long myID = IGC.Me; textSurface.WriteText(myID.ToString()+"\n", true); Me.CustomData = myID.ToString(); @@ -159,19 +231,19 @@ public void GetID() public void Loop() { - if(receiverSystem) { Receive(broadcastChannel); } - else if(checkingRange) //If we're not receiving and we are checking if a receiver is in range: + else if(checkingRange) { bool endpoint = IGC.IsEndpointReachable(receiverProgBlockID, TransmissionDistance.TransmissionDistanceMax); - if(endpoint & sentInrangeMessage == false) + if(endpoint && sentInrangeMessage == false) { - //this will immedeately trigger the timer (which you should have set up to turn on the Antenna and run the PB with the send command) so location is broadcasted. - timer.Trigger(); //This one will activate the Send option. + //this will immedeately trigger the send command so location is broadcasted. + _stateMachine = Send(broadcastChannel); + Runtime.UpdateFrequency |= UpdateFrequency.Once; sentInrangeMessage = true; textSurface.WriteText("Found Receiver in range! Sent co-ordinates immedeately!\n", false); //custom actions box when a reciever has been found in range @@ -183,4 +255,12 @@ public void Loop() sentInrangeMessage = false; } } -} \ No newline at end of file + if (!receiverSystem & tickCount == timeDelay & useInternalTimer == true) + { + tickCount = 0; + _stateMachine = Send(broadcastChannel); + Runtime.UpdateFrequency |= UpdateFrequency.Once; + } +} + +//Virtually Invisible Communication Protocol : VICP (looks a lot like VOIP) \ No newline at end of file