Showcase - WiP ESB - empyrion service bus (an MQTT framework)

Discussion in 'Empyrion API' started by imlarry425, Apr 28, 2023.

  1. imlarry425

    imlarry425 Captain

    Joined:
    Jan 10, 2019
    Messages:
    460
    Likes Received:
    338
    I've been working on something for the last couple of months that addresses a problem that @ASTIC brought up recently about the challenge of creating mods that require coordination between the client, dedi, and playfield servers as well as providing a mechanism for bidirectional communication with external services.

    While I'm happy with my progress I'm plodding along (I'm a db geek so this sort of coding is daunting) and wanted to share what I have working so far (as well as what isn't) to hopefully get some feedback.

    Here's the github link, if anybody feels like plowing through the readme, taking a look at the code and/or doing a build please feel free. Kindness appreciated.

    [​IMG]
    (so many butterflies!)
     
    #1
    Macros likes this.
  2. imlarry425

    imlarry425 Captain

    Joined:
    Jan 10, 2019
    Messages:
    460
    Likes Received:
    338
    The coordinate origin of an entity (core placement) is at (0,128,0) so relative component placement can be derived from that. Why not (0,0,0)? No idea; it's their world and we play in it, which is the way it's supposed to work. Never understood the killjoys who don't seem to get that.

    Talking with Bing (not Crosby, the other one) and am amazed that I got to live in this sort of brave new world.

    ***

    A block game API provides a set of classes to get information about things in the game:

    IEntity
    .Id returns the int id of an entity
    .Name returns the friendly name of a string
    .Type returns the type of entity as an enum
    .Structure returns a IStructure interface to the entity (if it has one, else null)
    IStructure
    .GetBlock(int x,int y,int z) returns an IBlock interface to the block in this structure at x/y/z (if it has one, else null)
    .MinPos returns a VectorInt3 of one corner of the structures bounding box
    .MaxPos returns a VectorInt3 of the opposite corner of the structures bounding box
    VectorInt3
    .Equals(VectorInt3 other) bool compare this VectorInt3 with another
    .x ret int x
    .y ret int y
    .z ret int z
    IBlock
    .Get(out int type, out int shape, out int rot, out bool active) returns the type, shape, rotation, and if this block is active

    Given an instance of IEntity, create a function that returns a dictionary<blockType,count> for the related structure if there is one. Use the bounding box and a brute force I/J/K approach.

    Here is a function that takes an IEntity instance as an input and returns a Dictionary<int, int> representing the count of each block type in the related structure if there is one:
    Code:
    public Dictionary<int, int> GetBlockTypeCount(IEntity entity)
    {
        Dictionary<int, int> blockTypeCount = new Dictionary<int, int>();
        IStructure structure = entity.Structure;
        if (structure != null)
        {
            VectorInt3 minPos = structure.MinPos;
            VectorInt3 maxPos = structure.MaxPos;
            for (int i = minPos.x; i <= maxPos.x; i++)
            {
                for (int j = minPos.y+128; j <= maxPos.y+128; j++) // !!! the y-axis is shifted so origin is (0,128,0)
                {
                    for (int k = minPos.z; k <= maxPos.z; k++)
                    {
                        IBlock block = structure.GetBlock(i, j+128, k);
                        if (block != null)
                        {
                            int type;
                            int shape;
                            int rot;
                            bool active;
                            block.Get(out type, out shape, out rot, out active);
                            if (blockTypeCount.ContainsKey(type))
                            {
                                blockTypeCount[type]++;
                            }
                            else
                            {
                                blockTypeCount[type] = 1;
                            }
                        }
                    }
                }
            }
        }
        return blockTypeCount;
    }
    ESB/Client/Gatetech.FlexionAmplifier.TestAPI/R {"0":519,"1036":2,"407":7,"1787":6,"1829":10,"406":19,"336":2,"2032":1,"1798":6,"1400":1,"2040":4,"1855":6,"344":1,"653":1,"1083":2,"1262":3,"623":1,"1559":3,"2042":1,"498":6,"1401":1,"259":1,"1260":1,"1881":8}

    0=air (dead space in bounding box), 1036=ShutterDoor1x2 (two blocks return this value for one door), 407=HullArmoredThinLarge, etc.

    Gotta make a gizmo to extract Block Id, Name, and SizeInBlocks from BlocksConfig.ecf to generate an enum and divide blocks with a thing by how many blocks are occupied by a thing... Why isn't the enum declare included in the ModApi package? 2nd verse, same as the first... maybe they build it at RT or don't use an enum at all.
     
    #2
    Last edited: Feb 7, 2024
  3. imlarry425

    imlarry425 Captain

    Joined:
    Jan 10, 2019
    Messages:
    460
    Likes Received:
    338
    Quick question- is anybody aware of what the "entityType" parameter (a string) for the IPlayfield.SpawnEntity() method is expecting? I've tried both prefab names and the EntityType tag from /Configuration/EClassConfig.ecf and it just quietly returns without actually doing anything or signaling an error.
     
    #3
  4. ASTIC

    ASTIC Captain

    Joined:
    Dec 11, 2016
    Messages:
    993
    Likes Received:
    707
    Take a look at here https://github.com/GitHub-TC/Empyri...bHost/Controllers/StructureController.cs#L104
     
    #4
    imlarry425 likes this.
  5. imlarry425

    imlarry425 Captain

    Joined:
    Jan 10, 2019
    Messages:
    460
    Likes Received:
    338
    I see what you're doing here. Were you able to get the non-vehicle entityTypeName param working to spawn specific types of animals (type=9) and faction NPCs (type=13, 22, 23, 26?) as the comment indicates?

    I didn't see NPCFighter in the EntityType enum and that's my ultimate goal. I'll do my own homework but shortcuts & hints are always appreciated. Thanks you and thanks for all the work you've put over on github- really clean code.
     
    #5
  6. ASTIC

    ASTIC Captain

    Joined:
    Dec 11, 2016
    Messages:
    993
    Likes Received:
    707
    I have not yet needed NPC Entites. But you can still change the fraction of an object after creating it. However, I doubt whether this will create a "flyable" NPC ship - there is probably a lot more magic in the internals.

    PS: Thanks for the compliment
     
    #6
  7. imlarry425

    imlarry425 Captain

    Joined:
    Jan 10, 2019
    Messages:
    460
    Likes Received:
    338
    I'm actually interested in expanding spawner functionality (i.e. a "StageManager" sort of thing) for the standard out-of-the-box spawnable entities .. alternate triggers, no spawn block required, timer based random encounters outdoors, better control indoors, player location relative positions and that sort of stuff. Fingers crossed, plenty of walls to beat my head against.
    Code:
    example: an NPCFighter entity from EClassConfig.ecf
    
    { +Entity Name: HexapodInfected, Ref: Hexapod
      Mesh: Enemies/HexapodInfectedPrefab
      EntityType: NPCFighter
      Faction: TheLegacy
      MeshDead: Enemies/HexapodInfectedDeadPrefab
      HeadTransform: Head
      SfxHurt: Enemies/TotalHorror/TotalHorror_SfxHurt
    }
     
    #7
    Last edited: May 16, 2023
  8. imlarry425

    imlarry425 Captain

    Joined:
    Jan 10, 2019
    Messages:
    460
    Likes Received:
    338
    [ MOVED FROM API Bugs thread per request from @Taelyn ]

    If you have a "look at this" suggestion for how to get the contents of ItemStack for a specific container in a structure via Request_??? I'd greatly appreciate it. I got it working in IModApi for single player...

    var content = _ctx.LoadedEntity[entityId].Structure.GetDevice<IContainer>(vector).GetContent())
    ...where vector is the pos, but this depends on having a dictionary of IEntity interfaces.

    For MP I can get these from OnPlayfieldLoaded() on the PF server (IPlayfield.Entities()) but I am not getting a PF load event in my mod for the starting playfield so I don't have an IPlayfield to make the call. Warping to new PFs does trigger the OnPlayfieldLoaded() so guessing this is a race condition with the mod starting in parallel with other PF server start tasks.

    Hey @ASTIC, thanks for looking ... I've realized I was inflicting the race condition on myself by improperly using async calls and declarations during Init() ... reworking all that so it is synchronous during startup. Sorry for wasting your time!
     
    #8
    Last edited: Feb 3, 2024
    Taelyn likes this.
  9. imlarry425

    imlarry425 Captain

    Joined:
    Jan 10, 2019
    Messages:
    460
    Likes Received:
    338
    Whew ... not as hard as I was afraid of. Thanks again for the "The event will be fired" ...
    Code:
    C:\Users\imlar>mosquitto_sub -v -t "PlayfieldServer/#"
    
    PlayfieldServer/I/Messenger.ConnectAsync/7d191eb6d1d/3 {"WithTcpServer":"localhost","Application":"PlayfieldServer","MachineId":"4786cdb589e1152b60281626b8e67b11272a62d5b80994b9e15d3943dbe69e09","ClientId":"7d191eb6d1d","ConnectedAt":"2024-02-02T21:03:00"}
    PlayfieldServer/I/ESB.GameManager.CreateLocalDatabase/7d191eb6d1d/4 {"DatabasePath":"C:\\SteamRoot\\steamapps\\common\\Empyrion - Galactic Survival\\Content\\Mods\\ESB\\Games\\RepoCoopGame\\local.db","Status":"AlreadyExists"}
    PlayfieldServer/E/Application.GameEnter/7d191eb6d1d/5 {"GameTicks":7803,"GameName":"RepoCoopGame","GameIdentifier":"RepoCoopGame","GameDataPath":"C:\\SteamRoot\\steamapps\\common\\Empyrion - Galactic Survival\\Content\\Mods\\ESB\\Games\\RepoCoopGame","GameMode":"PlayfieldServer"}
    PlayfieldServer/E/GameEvent.StarClassEntered/7d191eb6d1d/6 {"GameTicks":7810,"Mode":"PlayfieldServer","Arg1":"G2","Arg2":"TemperateStarter"}
    PlayfieldServer/E/Application.OnPlayfieldLoaded/7d191eb6d1d/7 {"GameTicks":7810,"Name":"Akua","PlayfieldType":"Moon","PlanetType":"TemperateStarter","PlanetClass":"Temperate","SolarSystemName":"Ellyon","SolarSystemCoordinates":{"X":13400000,"Y":2500000,"Z":12600000},"Entities":[{"Id":1001,"Name":"Abandoned Base","Type":"Proxy","Position":"(-3432.00, 22.50, 1624.00)"},{"Id":1002,"Name":"Old Com Array","Type":"Proxy","Position":"(-1976.00, 48.50, -1640.00)"},{"Id":1003,"Name":"Village","Type":"Proxy","Position":"(3640.00, 43.50, -1592.00)"},{"Id":1004,"Name":"Archery Range","Type":"Proxy","Position":"(3320.00, 44.50, -1512.00)"},{"Id":1005,"Name":"Guard Tower","Type":"Proxy","Position":"(3656.00, 44.50, -1144.00)"},{"Id":1009,"Name":"Font of Knowledge","Type":"Proxy","Position":"(1240.00, 47.50, 1144.00)"},{"Id":1010,"Name":"Camp (Font of Knowledge)","Type":"Proxy","Position":"(1256.00, 47.50, 1112.00)"},{"Id":1011,"Name":"Supply Depot","Type":"Proxy","Position":"(-2968.00, 31.50, 8.00)"},{"Id":1012,"Name":"Activator","Type":"Proxy","Position":"(3064.00, 29.50, 1688.00)"},{"Id":1013,"Name":"Activator","Type":"Proxy","Position":"(2568.00, 47.50, 312.00)"},{"Id":1014,"Name":"Farm","Type":"Proxy","Position":"(3016.00, 42.50, -1384.00)"},{"Id":1015,"Name":"Farm","Type":"Proxy","Position":"(2872.00, 37.50, -1720.00)"},{"Id":1016,"Name":"Farm","Type":"Proxy","Position":"(3032.00, 37.50, -1704.00)"},{"Id":1017,"Name":"Plantation","Type":"Proxy","Position":"(1448.00, 41.50, -1608.00)"},{"Id":1018,"Name":"Plantation","Type":"Proxy","Position":"(2376.00, 66.50, 136.00)"},{"Id":1019,"Name":"Plantation","Type":"Proxy","Position":"(2552.00, 35.50, -440.00)"},{"Id":1020,"Name":"Plantation","Type":"Proxy","Position":"(2408.00, 42.50, -616.00)"},{"Id":1021,"Name":"Plantation","Type":"Proxy","Position":"(2312.00, 36.50, -248.00)"},{"Id":1022,"Name":"Plantation","Type":"Proxy","Position":"(2440.00, 47.50, -856.00)"},{"Id":1023,"Name":"Plantation","Type":"Proxy","Position":"(2696.00, 146.50, -1224.00)"},{"Id":1024,"Name":"Plantation","Type":"Proxy","Position":"(2856.00, 43.50, -504.00)"},{"Id":1025,"Name":"Plantation","Type":"Proxy","Position":"(2408.00, 29.50, 888.00)"},{"Id":1026,"Name":"Plantation","Type":"Proxy","Position":"(2552.00, 37.50, 1672.00)"},{"Id":1027,"Name":"Plantation","Type":"Proxy","Position":"(2536.00, 33.50, 1368.00)"},{"Id":1028,"Name":"Plantation","Type":"Proxy","Position":"(2328.00, 27.50, 1096.00)"},{"Id":1029,"Name":"Plantation","Type":"Proxy","Position":"(2280.00, 40.50, -1544.00)"},{"Id":1030,"Name":"Tower","Type":"Proxy","Position":"(1832.00, 77.50, 520.00)"},{"Id":1031,"Name":"Ruins","Type":"Proxy","Position":"(2024.00, 94.50, 472.00)"},{"Id":1032,"Name":"Ruins","Type":"Proxy","Position":"(1768.00, 72.50, 568.00)"},{"Id":1033,"Name":"Small Wreckage","Type":"Proxy","Position":"(3752.00, 53.60, -904.00)"},{"Id":1034,"Name":"Small Wreckage","Type":"Proxy","Position":"(-2568.00, 50.50, -1128.00)"},{"Id":1035,"Name":"Small Wreckage","Type":"Proxy","Position":"(-776.00, 45.50, 984.00)"},{"Id":1037,"Name":"Small Wreckage","Type":"Proxy","Position":"(-2344.00, 53.60, -1048.00)"},{"Id":1038,"Name":"Tech Ruins","Type":"Proxy","Position":"(1256.00, 30.50, -1208.00)"},{"Id":1046,"Name":"EscapePod","Type":"EscapePod","Position":"(-1.54, 55.89, -6.67)"},{"Id":1006,"Name":"Obelisk","Type":"Proxy","Position":"(8.00, 44.00, 536.00)"},{"Id":1007,"Name":"Scientist Camp","Type":"Proxy","Position":"(56.00, 44.50, 584.00)"},{"Id":1008,"Name":"Artifact Site","Type":"Proxy","Position":"(-648.00, 8.50, -984.00)"},{"Id":1036,"Name":"Small Wreckage","Type":"Proxy","Position":"(-616.00, 42.50, -696.00)"}],"IsPvP":false}
    
     
    #9

Share This Page