Track.cs

For easier viewing download code from my code page or copy and paste into your IDE.

using UnityEngine;
using System.Text;
using System.Net.Sockets;
using TMPro;
using System.Collections;

public class Track : MonoBehaviour
{
// Start is called before the first frame update

private Train[] trains;
public Train clientTrain { get; private set; }
public bool FinishedSetup { get; private set; }

private Section[] sections;
private Turnout[] turnouts;
private Waypoint firstPoint;

public Transform spawn;
public GameObject clientPrefab;//couldnt force color to update at runtime so one prefab has blue username other has red
public GameObject otherPrefab;
public SpedOMeter spedOMeter;
public Client myClient;
public TextMeshProUGUI failed;
public TextMeshProUGUI deadlocked;
void Awake()
{
    Debug.Log("Starting Track Awake");
    //spawn = s.position;
    //spawnTrain();
    Waypoint[] wps = FindObjectsOfType<Waypoint>();
    foreach (Waypoint w in wps) 
    { 
        if (w.id == 1)
        { 
            firstPoint = w; 
            break; 
        } 
    }

    sections = FindObjectsOfType<Section>();
    turnouts = FindObjectsOfType<Turnout>();
    //sorting
    Section[] _sects = new Section[sections.Length];
    Turnout[] _turns = new Turnout[turnouts.Length];

    foreach(Section s in sections)
    {
        _sects[s.order] = s;
    }
    sections = _sects;

    foreach (Turnout t in turnouts)
    {
        _turns[t.order] = t;
    }
    turnouts = _turns;

    trains = new Train[INetwork_Utils.MAX_TRAINS];//empty array to start

    //Debug.Log("number of sections found: " + sections.Length);
    //foreach (Section s in sections)
    //{
    //    Debug.Log("This sections id is: "+s.getID()+"\n"+"This sections path:"+s.getPath());
    //}
    Debug.Log("Finished Track Awake");
}

void Start()
{
    Debug.Log("Starting Track Start()");
    DoClientSetup();
    Debug.Log("Finished Track Start() should have touched server");
}

// Update is called once per frame
void Update()
{
    if (!FinishedSetup)
        return;
    //Debug.Log("Entered Track Update()");
    foreach (Train t in trains)
    {
        if(!(t is null))
        {
            int sect = t.GetSectionIndex();
            if (t.isReady())//check if we need to get next
            {//getting next
             //Debug.Log("Reached index: " + sections[i].myP.getCur() + " in section: " + i);
                t.resetReady();
                Waypoint next = sections[sect].Next();
                if (next is null)//at end of path
                {
                    ChangeSections(t, sections[sect].getNextSection(), sect);//train, where we are going, where we are/were
                }
                else//Do not need to leave yet
                    t.Move(next);
            }
            else
                t.Move(sections[sect].getCur());//keep going at cur
        }
    }

}

private void ChangeSections(Train t, int next, int last)
{
    Section nextSection = sections[next];
    Section lastSection = sections[last];

    int ep;
    if (lastSection.isReversed())
        ep = 0;
    else
        ep = 1;

    bool passingThrough = false;
    if (lastSection.myEndpoints[ep] is Passthrough)
        passingThrough = true;

    if (!nextSection.isLocked())//this is the start of a critical region when working with race conditions this will need to be protected
    {
        #region Next Section Reverse or Not
        int sect_orientation;
        if (nextSection.getOrientationActual() == lastSection.myEndpoints[ep].Direction)
        { 
            nextSection.Reverse(false);
            sect_orientation = INetwork_Utils.CLOSED_NORM;
        }
        else
        { 
            nextSection.Reverse(true);
            sect_orientation = INetwork_Utils.CLOSED_REVERSE;
        }
            #endregion

        #region Restart Train
        if (t.isStopped())//direction you are entering from
        {
                //Debug.Log("Train " + t.getID() + "is starting again");
            t.restart();
            if (t.ID == clientTrain.ID)
            {
                spedOMeter.Reactivate();
            }
            spedOMeter.UpdateText();
        }
        #endregion

        #region Locking inaccessable turnouts
        if (t.ID == clientTrain.ID)
        {
            LockTurnouts(last, 0);
            LockTurnouts(last, 1);
        }
        #endregion

        lastSection.Exit();
        nextSection.Enter();

        t.SetSectionIndex(next);

        if (t.ID == clientTrain.ID && CheckDeadlock(nextSection))//this should work in a queue based priority last one in will get dropped because only runs on clientTrain (assumes close enough simulations)
        {
            RemoveTrain(clientTrain.ID, next);
            deadlocked.alpha = 255;
            StartCoroutine(DeadlockDisconnect(3));//disconnect will occur in 3 seconds
        }
        if (t.ID == clientTrain.ID) 
            myClient.MySend(INetwork_Utils.ENTER + INetwork_Utils.DELIM + next + INetwork_Utils.DELIM + last + INetwork_Utils.DELIM + sect_orientation);
    }
    else if (!t.isStopped())//next is locked hault the train
    {
        if (passingThrough && (lastSection.myEndpoints[ep].getNext() == lastSection.order))//if its pointing to itself
        { 
            ((Passthrough)(lastSection.myEndpoints[ep])).swapDirections();//try letting the train through
        }

        t.hault();
        if(t.ID == clientTrain.ID)
            spedOMeter.Deactivate();
    }
}

private bool CheckDeadlock(Section sectionIn)
{
    Endpoint exit;
    if (sectionIn.isReversed())
        exit = sectionIn.myEndpoints[0];
    else
        exit = sectionIn.myEndpoints[1];

    for (int i = 0; i < exit.destinations.Length; i++)
    {
        if (exit.destinations[i] != -1 && exit.destinations[i] != sectionIn.order )//if it dosnt go anywhere or is the one we are in then we dont evaluate
        {
            if (!(sections[exit.destinations[i]].isLocked()))
            { Debug.Log("No one in next section"); return false; }//no one in the coming section not deadlocked
            if (sections[exit.destinations[i]].IsNormalEntrance(exit) && !sections[exit.destinations[i]].isReversed())//if this is the entrance of the coming section and the train inside is moving normally (entrance to exit)
            { Debug.Log("train is moving away"); return false; }//the train in the other section is moving away and will clear shortly
        }
    }
    return true;
}

private void LockTurnouts(int sect, int toClose)
{
    //bool closeit = true;
    //int[] directionsInTurnout = sections[sect].myEndpoints[toClose].getSections();//
    //foreach (int i in directionsInTurnout)
    //{
    //    if (i != -1 && sections[i].isLocked() && i != sect)
    //    { Debug.Log("section " + i + " is still using this turnout " + toClose); closeit = false; break; }//one of the other sections needs this turnout active
    //}
    //if (closeit)
    //    sections[sect].myEndpoints[toClose].deactivate();
    if (sections[sect].myEndpoints[toClose] is null)
        return;
    sections[sect].myEndpoints[toClose].deactivate();
}
private int SpawnTrainOnTrack(int trainID, int targetID, int sectionId, Vector3 position, string name, int speed)
{
    Debug.Log("Spawning the other users trains");
    Waypoint target = sections[sectionId].GetWaypointByID(targetID);
    GameObject obj = new GameObject("");
    obj.transform.position = position;
    GameObject newObject = Instantiate(otherPrefab, obj.transform);
    Train newTrain = newObject.GetComponent<Train>();
    bool added = false;
    int i;
    for (i = 0; i < trains.Length; i++)
    {
        if (trains[i] is null)
        {
            added = true;
            trains[i] = newTrain;
            trains[i].ID = trainID;
            trains[i].SetSectionIndex(sectionId);
            sections[sectionId].StartingWithinSectionAt(targetID);
            trains[i].Username.text = name;
            trains[i].SpeedChange(speed);
            break;
        }
    }
    Debug.Log("finished with old train");
    if (added)
        return trains[i].ID;
    else
        return -1;
}

private void SpawnMyTrain()
{

    //while (sections[0].isLocked())
    //{
    //write to UI waiting to spawn 
    //Debug.Log("couldnt spawn, waiting 5 sec");
    //  System.Threading.Thread.Sleep(5000);
    //}
    //if wrote to UI remove it and then complete spawn

    Debug.Log("Spawning this client's train");
    GameObject newObject = Instantiate(clientPrefab, spawn);
    Train newTrain = newObject.GetComponent<Train>();
    if (clientTrain is null)
    { 
        clientTrain = newTrain;
        Debug.Log(PlayerPrefs.GetString("username"));
        clientTrain.Username.text = PlayerPrefs.GetString("username");
    }
    bool added = false;
    int i;
    for (i = 0; i < trains.Length; i++)
    {
        if (trains[i] is null)
        {
            added = true;
            trains[i] = newTrain;
            trains[i].SetTarget(firstPoint);
            break;
        }
    }
    sections[18].Enter();//enter the staging section
    if (added)
    { myClient.MySend(INetwork_Utils.NEW_TRAIN + INetwork_Utils.DELIM + clientTrain.ID + INetwork_Utils.DELIM + clientTrain.Speed + INetwork_Utils.DELIM + clientTrain.Username.text.ToString()); }
    else
    { Debug.Log("Couldnt add the train...track must be full"); }
}
private void SpawnOtherTrainSpawn(int id, int speed, string username)
{

    //while (sections[0].isLocked())
    //{
    //write to UI waiting to spawn 
    //Debug.Log("couldnt spawn, waiting 5 sec");
    //  System.Threading.Thread.Sleep(5000);
    //}
    //if wrote to UI remove it and then complete spawn

    Debug.Log("New user joined");
    GameObject newObject = Instantiate(otherPrefab, spawn);
    Train newTrain = newObject.GetComponent<Train>();
    int i;
    for (i = 0; i < trains.Length; i++)
    {
        if (trains[i] is null)
        {
            trains[i] = newTrain;
            trains[i].ID = id;
            trains[i].SpeedChange(speed);
            trains[i].Username.text = username;
            trains[i].SetTarget(firstPoint);
            break;
        }
    }
    sections[18].Enter();
}
public void ClientSpeedChanged() 
{
    int s = (int)(spedOMeter.MySlider.value);
    clientTrain.SpeedChange(s);
    spedOMeter.UpdateText();
    myClient.MySend(INetwork_Utils.SPEED + INetwork_Utils.DELIM + clientTrain.ID + INetwork_Utils.DELIM + s);
}

public void HandleData(byte[] data)
{
    string msg = ASCIIEncoding.ASCII.GetString(data);
    Debug.Log("made it to the handle data function");
    int i, val, act;
    string[] arr = msg.Split(INetwork_Utils.DELIM);
    if (!int.TryParse(arr[0], out act))
        Debug.Log("Message corrupted didnt find the action\n");
    else if (arr[0].Equals(INetwork_Utils.NONE))
    { myClient.MySend(INetwork_Utils.NONE); return; }

    switch (act)
    {
        #region Turnouts
        case INetwork_Utils.TURN://TURN;index;direction
            Debug.Log("In Turnout changed");
            if (arr.Length == 3 && int.TryParse(arr[1], out i) && int.TryParse(arr[2], out val))
                AdjustTurnout(i, val);
            else
            {
                Debug.Log("Message corrupted couldnt change turnout");
                //tell the server
            }
            break;
        #endregion
        #region Speeds
        case INetwork_Utils.SPEED://SPEED;index;speed
            Debug.Log("In Speed change");
            if (arr.Length == 3 && int.TryParse(arr[1], out i) && int.TryParse(arr[2], out val))
                ChangeSpeed(i, val);
            else
            {
                Debug.Log("Message corrupted couldnt update speed");
                //tell the server
            }
            break;
        #endregion
        #region Section Change CODE NOT WRITTEN
        case INetwork_Utils.ENTER://ENTER;index in;index out
            break;
        #endregion
        #region Location Requested
        case INetwork_Utils.LOC:
            SendLocation();
            break;
        #endregion
        #region New Train
        case INetwork_Utils.NEW_TRAIN://NEW_TRAIN;id;speed;name
            Debug.Log("In New Train");
            i = int.Parse(arr[1]);
            val = int.Parse(arr[2]);
            string name;
            if (arr.Length == 4)
                name = arr[3];
            else
                name = "Didnt get the name";
            SpawnOtherTrainSpawn(i,val,name);

            break;
        #endregion
        #region Disconnection
        case INetwork_Utils.DISCONNECT:
            RemoveTrain(int.Parse(arr[1]),int.Parse(arr[2]));
            break;
        #endregion
    }
}
private void DoClientSetup()
{
    myClient.MySend(INetwork_Utils.NONE);//send an empty message just to establish a connection
    string turns, sects, trs;
    try
    {
        turns = myClient.MyRecieve();
        sects = myClient.MyRecieve();
        trs = myClient.MyRecieve();
    }
    catch (SocketException)
    {
        failed.alpha = 255;
        return;
    }

    if (trs.Equals(INetwork_Utils.DISCONNECT) || turns.Equals(INetwork_Utils.DISCONNECT))
    {
        failed.alpha = 255;
        return;
    }

    SetupSections(sects);
    SetupTurns(turns);
    SetupTrains(trs);
    FinishedSetup = true;
    Debug.Log("Finished Client Setup");
}

private void SetupSections(string data)
{
    string[] splitData = data.Split(INetwork_Utils.DELIM);
    string msg="";
    foreach (string s in splitData)
        msg += " " + s;
    //Debug.Log("section data after split " + msg);
    for (int i = 0; i < splitData.Length-1; i++)
    {
        int sect_data = int.Parse(splitData[i]);
        if (sect_data != INetwork_Utils.OPEN)
        {
            if (sect_data == INetwork_Utils.CLOSED_REVERSE)
            { 
                sections[i].Reverse(true);
                Debug.Log("Set section " + i + " to be reversed");
            }
            sections[i].Lock(); 
        }
        else
            sections[i].Unlock();
    }
}
private void SetupTurns(string data)
{
    string[] splitData = data.Split(INetwork_Utils.DELIM);
    for (int i = 0; i < splitData.Length; i++)
    {
        int val;
        if(int.TryParse(splitData[i], out val))
            AdjustTurnout(i, val);
    }
}

private void SetupTrains(string data)
{//train id;target id;section id;Vector3 dir (x,y,z);Username;speed
    Debug.Log(data);
    if (data.Equals(INetwork_Utils.NONE))
    { 
        SpawnMyTrain();//no other trains on track
        return;
    }

    string[] trainData = data.Split(INetwork_Utils.DELIM);
    for (int i = 0; i <= trainData.Length - 6; i += 6)//train id; waypoint id; section id; Vector3 position; username; speed
    {
        int trainID = int.Parse(trainData[i]);
        int waypointID = int.Parse(trainData[i + 1]);
        int sectionID = int.Parse(trainData[i + 2]);
        string[] splitString = trainData[i + 3].Trim(new char[] { '(', ')' }).Split(',');
        Vector3 position = new Vector3(float.Parse(splitString[0]), float.Parse(splitString[1]), float.Parse(splitString[2]));
        string name = trainData[i + 4];
        int speed = int.Parse(trainData[i + 5]);
        SpawnTrainOnTrack(trainID, waypointID, sectionID, position, name, speed);
    }
    //SpawnTrainOnTrack(trainID, waypointID, sectionID, dir, name);
    SpawnMyTrain();//spawn other train then ours
}
private void AdjustTurnout(int id, int dir)
{
    Turnout changed = turnouts[id];
    changed.RotateTurnout(dir, changed.Direction);
    changed.Direction = dir;
}

private void ChangeSpeed(int id, int s)
{
    foreach (Train t in trains)
    {
        if (!(t is null)&&t.ID == id)
        { t.SpeedChange(s); Debug.Log("found the train changing speed"); }
    }
}

private void SendLocation()
{
    myClient.MySend(clientTrain.getLocation());
}

private void RemoveTrain(int id, int section_id) 
{
    for (int i = 0; i < trains.Length; i++)
    {
        if (!(trains[i] is null) && trains[i].ID == id)
        {
            Debug.Log("Found the train to remove");
            Train disconnected = trains[i];
            Destroy(disconnected.transform.gameObject);
            Debug.Log("Child count of the transform.parent " + disconnected.transform.parent.childCount);
            trains[i] = null;
            if (trains[i] is null)
                Debug.Log("Set train " + i + "to null");
            else
                Debug.Log("null didnt work");
            //make sure to clear the section

            sections[section_id].Exit();
            Debug.Log("Cleared out section " + section_id);
        }
    }
}
public void Turned(int index, int dir)
{
    myClient.MySend(INetwork_Utils.TURN + INetwork_Utils.DELIM + index + INetwork_Utils.DELIM + dir);
}

IEnumerator DeadlockDisconnect(float waitTime)
{
    Time.timeScale = 0f;
    yield return new WaitForSecondsRealtime(waitTime); // Use WaitForSecondsRealtime to ignore timeScale
    Debug.Log("Calling app.quit");
    Application.Quit();
}

}

css.php