build: C# 6 compatible port + build script + csproj

- Convert all C# 7/8 features to C# 6 for mono mcs compatibility:
  - Tuple switch → if-else chain
  - Switch expressions → if-else blocks
  - Expression-bodied switch → regular switch statement
- Verified compilation succeeds (only missing game DLL refs)
- Added mod/ONIAgentBridge.csproj for dotnet SDK builds
- Added scripts/build_mod.sh for mono-based builds
  - Auto-detects ONI installation path
  - References required game DLLs
  - Outputs to mod/bin/
This commit is contained in:
root
2026-05-22 10:39:17 +08:00
parent 6a71d42f78
commit 64e57b2c44
4 changed files with 721 additions and 231 deletions

View File

@ -76,298 +76,481 @@ namespace ONIAgentBridge
string responseJson;
switch (path, method)
{
bool _handled = false;
// --- Health ---
case ("/health", "GET"):
if (path == "/health" && method == "GET")
{
_handled = true;
responseJson = JsonSerializer.Serialize(new { status = "ok", service = "oni-agent-bridge" });
break;
}
// --- State Queries ---
case ("/api/state/game", "GET"):
if (path == "/api/state/game" && method == "GET")
{
_handled = true;
responseJson = GetGameState();
break;
case ("/api/state/resources", "GET"):
}
if (path == "/api/state/resources" && method == "GET")
{
_handled = true;
responseJson = GetResources();
break;
case ("/api/state/duplicants", "GET"):
}
if (path == "/api/state/duplicants" && method == "GET")
{
_handled = true;
responseJson = GetDuplicants();
break;
case ("/api/state/buildings", "GET"):
}
if (path == "/api/state/buildings" && method == "GET")
{
_handled = true;
responseJson = GetBuildings();
break;
case ("/api/state/research", "GET"):
}
if (path == "/api/state/research" && method == "GET")
{
_handled = true;
responseJson = GetResearch();
break;
case ("/api/state/research/detail", "GET"):
}
if (path == "/api/state/research/detail" && method == "GET")
{
_handled = true;
responseJson = GetResearchDetail();
break;
case ("/api/state/building_detail", "GET"):
}
if (path == "/api/state/building_detail" && method == "GET")
{
_handled = true;
responseJson = GetBuildingDetail(query);
break;
case ("/api/state/geysers", "GET"):
}
if (path == "/api/state/geysers" && method == "GET")
{
_handled = true;
responseJson = GetGeysers();
break;
case ("/api/state/alert", "GET"):
}
if (path == "/api/state/alert" && method == "GET")
{
_handled = true;
responseJson = GetAlerts();
break;
case ("/api/state/critters", "GET"):
}
if (path == "/api/state/critters" && method == "GET")
{
_handled = true;
responseJson = GetCritters();
break;
case ("/api/state/plants", "GET"):
}
if (path == "/api/state/plants" && method == "GET")
{
_handled = true;
responseJson = GetPlants();
break;
case ("/api/state/rooms", "GET"):
}
if (path == "/api/state/rooms" && method == "GET")
{
_handled = true;
responseJson = GetRooms();
break;
case ("/api/state/queue", "GET"):
}
if (path == "/api/state/queue" && method == "GET")
{
_handled = true;
responseJson = GetTaskQueue(query);
break;
case ("/api/state/priorities", "GET"):
}
if (path == "/api/state/priorities" && method == "GET")
{
_handled = true;
responseJson = GetPriorities();
break;
case ("/api/state/events", "GET"):
}
if (path == "/api/state/events" && method == "GET")
{
_handled = true;
responseJson = GetEvents(query);
break;
case ("/api/state/power", "GET"):
}
if (path == "/api/state/power" && method == "GET")
{
_handled = true;
responseJson = GetPowerGrid();
break;
case ("/api/state/pipes", "GET"):
}
if (path == "/api/state/pipes" && method == "GET")
{
_handled = true;
responseJson = GetPipes(query);
break;
case ("/api/state/co2", "GET"):
}
if (path == "/api/state/co2" && method == "GET")
{
_handled = true;
responseJson = GetCO2();
break;
case ("/api/state/temperature/zones", "GET"):
}
if (path == "/api/state/temperature/zones" && method == "GET")
{
_handled = true;
responseJson = GetTempZones();
break;
case ("/api/state/morale", "GET"):
}
if (path == "/api/state/morale" && method == "GET")
{
_handled = true;
responseJson = GetMorale();
break;
case ("/api/state/diseases", "GET"):
}
if (path == "/api/state/diseases" && method == "GET")
{
_handled = true;
responseJson = GetDiseases();
break;
case ("/api/state/storage", "GET"):
}
if (path == "/api/state/storage" && method == "GET")
{
_handled = true;
responseJson = GetStorage();
break;
case ("/api/state/duplicants/skills", "GET"):
}
if (path == "/api/state/duplicants/skills" && method == "GET")
{
_handled = true;
responseJson = GetDuplicantSkills();
break;
case ("/api/state/saves", "GET"):
}
if (path == "/api/state/saves" && method == "GET")
{
_handled = true;
responseJson = GetSaves();
break;
case ("/api/state/buildable", "GET"):
}
if (path == "/api/state/buildable" && method == "GET")
{
_handled = true;
responseJson = GetBuildable();
break;
case ("/api/state/printing_pod", "GET"):
}
if (path == "/api/state/printing_pod" && method == "GET")
{
_handled = true;
responseJson = GetPrintingPod();
break;
case ("/api/state/atmo_suits", "GET"):
}
if (path == "/api/state/atmo_suits" && method == "GET")
{
_handled = true;
responseJson = GetAtmoSuits();
break;
case ("/api/state/sensors", "GET"):
}
if (path == "/api/state/sensors" && method == "GET")
{
_handled = true;
responseJson = GetSensors();
break;
case ("/api/state/overlay", "GET"):
}
if (path == "/api/state/overlay" && method == "GET")
{
_handled = true;
responseJson = GetOverlay();
break;
}
// --- Cell-level map data ---
case ("/api/state/cell", "GET"):
if (path == "/api/state/cell" && method == "GET")
{
_handled = true;
responseJson = GetCell(query);
break;
case ("/api/state/cells", "GET"):
}
if (path == "/api/state/cells" && method == "GET")
{
_handled = true;
responseJson = GetCells(query);
break;
case ("/api/state/cells/slice", "GET"):
}
if (path == "/api/state/cells/slice" && method == "GET")
{
_handled = true;
responseJson = GetCellSlice(query);
break;
case ("/api/state/gas", "GET"):
}
if (path == "/api/state/gas" && method == "GET")
{
_handled = true;
responseJson = GetGas(query);
break;
}
// --- Entity Registry (for AI reference) ---
case ("/api/registry/buildings", "GET"):
if (path == "/api/registry/buildings" && method == "GET")
{
_handled = true;
responseJson = GetBuildingRegistry();
break;
case ("/api/registry/elements", "GET"):
}
if (path == "/api/registry/elements" && method == "GET")
{
_handled = true;
responseJson = GetElementRegistry();
break;
case ("/api/registry/techs", "GET"):
}
if (path == "/api/registry/techs" && method == "GET")
{
_handled = true;
responseJson = GetTechRegistry();
break;
case ("/api/registry/priorities", "GET"):
}
if (path == "/api/registry/priorities" && method == "GET")
{
_handled = true;
responseJson = GetPriorityRegistry();
break;
}
// --- Actions ---
case ("/api/action/dig", "POST"):
if (path == "/api/action/dig" && method == "POST")
{
_handled = true;
responseJson = ExecuteDig(ctx);
break;
case ("/api/action/build", "POST"):
}
if (path == "/api/action/build" && method == "POST")
{
_handled = true;
responseJson = ExecuteBuild(ctx);
break;
case ("/api/action/deconstruct", "POST"):
}
if (path == "/api/action/deconstruct" && method == "POST")
{
_handled = true;
responseJson = ExecuteDeconstruct(ctx);
break;
case ("/api/action/prioritize", "POST"):
}
if (path == "/api/action/prioritize" && method == "POST")
{
_handled = true;
responseJson = ExecutePrioritize(ctx);
break;
case ("/api/action/research", "POST"):
}
if (path == "/api/action/research" && method == "POST")
{
_handled = true;
responseJson = ExecuteResearch(ctx);
break;
case ("/api/action/schedule", "POST"):
}
if (path == "/api/action/schedule" && method == "POST")
{
_handled = true;
responseJson = ExecuteSchedule(ctx);
break;
case ("/api/action/wardrobe", "POST"):
}
if (path == "/api/action/wardrobe" && method == "POST")
{
_handled = true;
responseJson = ExecuteWardrobe(ctx);
break;
case ("/api/action/mop", "POST"):
}
if (path == "/api/action/mop" && method == "POST")
{
_handled = true;
responseJson = ExecuteMop(ctx);
break;
case ("/api/action/harvest", "POST"):
}
if (path == "/api/action/harvest" && method == "POST")
{
_handled = true;
responseJson = ExecuteHarvest(ctx);
break;
case ("/api/action/cancel", "POST"):
}
if (path == "/api/action/cancel" && method == "POST")
{
_handled = true;
responseJson = ExecuteCancel(ctx);
break;
case ("/api/action/batch", "POST"):
}
if (path == "/api/action/batch" && method == "POST")
{
_handled = true;
responseJson = ExecuteBatch(ctx);
break;
case ("/api/action/priority_global", "POST"):
}
if (path == "/api/action/priority_global" && method == "POST")
{
_handled = true;
responseJson = ExecutePriorityGlobal(ctx);
break;
case ("/api/action/priority_type", "POST"):
}
if (path == "/api/action/priority_type" && method == "POST")
{
_handled = true;
responseJson = ExecutePriorityType(ctx);
break;
case ("/api/action/pause", "POST"):
}
if (path == "/api/action/pause" && method == "POST")
{
_handled = true;
responseJson = ExecutePause(ctx);
break;
case ("/api/action/unpause", "POST"):
}
if (path == "/api/action/unpause" && method == "POST")
{
_handled = true;
responseJson = ExecuteUnpause(ctx);
break;
case ("/api/action/speed", "POST"):
}
if (path == "/api/action/speed" && method == "POST")
{
_handled = true;
responseJson = ExecuteSpeed(ctx);
break;
case ("/api/action/save", "POST"):
}
if (path == "/api/action/save" && method == "POST")
{
_handled = true;
responseJson = ExecuteSave(ctx);
break;
case ("/api/action/save_as", "POST"):
}
if (path == "/api/action/save_as" && method == "POST")
{
_handled = true;
responseJson = ExecuteSaveAs(ctx);
break;
case ("/api/action/load", "POST"):
}
if (path == "/api/action/load" && method == "POST")
{
_handled = true;
responseJson = ExecuteLoad(ctx);
break;
case ("/api/action/assign_job", "POST"):
}
if (path == "/api/action/assign_job" && method == "POST")
{
_handled = true;
responseJson = ExecuteAssignJob(ctx);
break;
case ("/api/action/build_pipe", "POST"):
}
if (path == "/api/action/build_pipe" && method == "POST")
{
_handled = true;
responseJson = ExecuteBuildPipe(ctx);
break;
case ("/api/action/build_wire", "POST"):
}
if (path == "/api/action/build_wire" && method == "POST")
{
_handled = true;
responseJson = ExecuteBuildWire(ctx);
break;
case ("/api/action/screenshot", "POST"):
}
if (path == "/api/action/screenshot" && method == "POST")
{
_handled = true;
responseJson = ExecuteScreenshot(ctx);
break;
case ("/api/action/camera", "POST"):
}
if (path == "/api/action/camera" && method == "POST")
{
_handled = true;
responseJson = ExecuteCamera(ctx);
break;
case ("/api/action/toggle", "POST"):
}
if (path == "/api/action/toggle" && method == "POST")
{
_handled = true;
responseJson = ExecuteToggle(ctx);
break;
case ("/api/action/set_recipe", "POST"):
}
if (path == "/api/action/set_recipe" && method == "POST")
{
_handled = true;
responseJson = ExecuteSetRecipe(ctx);
break;
case ("/api/action/empty", "POST"):
}
if (path == "/api/action/empty" && method == "POST")
{
_handled = true;
responseJson = ExecuteEmpty(ctx);
break;
case ("/api/action/cancel_errand", "POST"):
}
if (path == "/api/action/cancel_errand" && method == "POST")
{
_handled = true;
responseJson = ExecuteCancelErrand(ctx);
break;
case ("/api/action/research_cancel", "POST"):
}
if (path == "/api/action/research_cancel" && method == "POST")
{
_handled = true;
responseJson = ExecuteResearchCancel(ctx);
break;
case ("/api/action/set_building_priority", "POST"):
}
if (path == "/api/action/set_building_priority" && method == "POST")
{
_handled = true;
responseJson = ExecuteSetBuildingPriority(ctx);
break;
case ("/api/action/set_automation", "POST"):
}
if (path == "/api/action/set_automation" && method == "POST")
{
_handled = true;
responseJson = ExecuteSetAutomation(ctx);
break;
case ("/api/action/printing_pod_select", "POST"):
}
if (path == "/api/action/printing_pod_select" && method == "POST")
{
_handled = true;
responseJson = ExecutePrintingPodSelect(ctx);
break;
case ("/api/action/critter_attack", "POST"):
}
if (path == "/api/action/critter_attack" && method == "POST")
{
_handled = true;
responseJson = ExecuteCritterAttack(ctx);
break;
case ("/api/action/door_lock", "POST"):
}
if (path == "/api/action/door_lock" && method == "POST")
{
_handled = true;
responseJson = ExecuteDoorLock(ctx);
break;
case ("/api/action/door_one_way", "POST"):
}
if (path == "/api/action/door_one_way" && method == "POST")
{
_handled = true;
responseJson = ExecuteDoorOneWay(ctx);
break;
case ("/api/action/dupe_move", "POST"):
}
if (path == "/api/action/dupe_move" && method == "POST")
{
_handled = true;
responseJson = ExecuteDupeMove(ctx);
break;
case ("/api/action/dupe_cancel_task", "POST"):
}
if (path == "/api/action/dupe_cancel_task" && method == "POST")
{
_handled = true;
responseJson = ExecuteDupeCancelTask(ctx);
break;
case ("/api/action/critter_wrangle", "POST"):
}
if (path == "/api/action/critter_wrangle" && method == "POST")
{
_handled = true;
responseJson = ExecuteCritterWrangle(ctx);
break;
case ("/api/action/plant_uproot", "POST"):
}
if (path == "/api/action/plant_uproot" && method == "POST")
{
_handled = true;
responseJson = ExecutePlantUproot(ctx);
break;
case ("/api/action/storage_filter", "POST"):
}
if (path == "/api/action/storage_filter" && method == "POST")
{
_handled = true;
responseJson = ExecuteStorageFilter(ctx);
break;
case ("/api/action/door_open", "POST"):
}
if (path == "/api/action/door_open" && method == "POST")
{
_handled = true;
responseJson = ExecuteDoorOpen(ctx);
break;
case ("/api/action/sensor_threshold", "POST"):
}
if (path == "/api/action/sensor_threshold" && method == "POST")
{
_handled = true;
responseJson = ExecuteSensorThreshold(ctx);
break;
case ("/api/action/sweep", "POST"):
}
if (path == "/api/action/sweep" && method == "POST")
{
_handled = true;
responseJson = ExecuteSweep(ctx);
break;
case ("/api/action/disinfect", "POST"):
}
if (path == "/api/action/disinfect" && method == "POST")
{
_handled = true;
responseJson = ExecuteDisinfect(ctx);
break;
case ("/api/action/clear", "POST"):
}
if (path == "/api/action/clear" && method == "POST")
{
_handled = true;
responseJson = ExecuteClear(ctx);
break;
case ("/api/action/rotate", "POST"):
}
if (path == "/api/action/rotate" && method == "POST")
{
_handled = true;
responseJson = ExecuteRotate(ctx);
break;
case ("/api/action/copy_settings", "POST"):
}
if (path == "/api/action/copy_settings" && method == "POST")
{
_handled = true;
responseJson = ExecuteCopySettings(ctx);
break;
case ("/api/action/overlay", "POST"):
}
if (path == "/api/action/overlay" && method == "POST")
{
_handled = true;
responseJson = ExecuteOverlay(ctx);
break;
case ("/api/action/dupe_personal_priority", "POST"):
}
if (path == "/api/action/dupe_personal_priority" && method == "POST")
{
_handled = true;
responseJson = ExecuteDupePersonalPriority(ctx);
break;
case ("/api/action/battery_charge", "POST"):
}
if (path == "/api/action/battery_charge" && method == "POST")
{
_handled = true;
responseJson = ExecuteBatteryCharge(ctx);
break;
case ("/api/action/valve_flow", "POST"):
}
if (path == "/api/action/valve_flow" && method == "POST")
{
_handled = true;
responseJson = ExecuteValveFlow(ctx);
break;
case ("/api/action/vent_pressure", "POST"):
}
if (path == "/api/action/vent_pressure" && method == "POST")
{
_handled = true;
responseJson = ExecuteVentPressure(ctx);
break;
case ("/api/action/incubator_setting", "POST"):
}
if (path == "/api/action/incubator_setting" && method == "POST")
{
_handled = true;
responseJson = ExecuteIncubatorSetting(ctx);
break;
case ("/api/action/fridge_temp", "POST"):
}
if (path == "/api/action/fridge_temp" && method == "POST")
{
_handled = true;
responseJson = ExecuteFridgeTemp(ctx);
break;
}
default:
if (!_handled)
{
ctx.Response.StatusCode = 404;
responseJson = JsonSerializer.Serialize(new { error = "not_found", path = path, method = method });
break;
}
var buffer = Encoding.UTF8.GetBytes(responseJson);
@ -1011,18 +1194,17 @@ namespace ONIAgentBridge
var levels = new List<object>();
for (int i = 1; i <= 9; i++)
{
string label = i switch
{
1 => "Lowest (only idle dupes)",
2 => "Very Low",
3 => "Low",
4 => "Below Normal",
5 => "Normal (default)",
6 => "Above Normal",
7 => "High",
8 => "Very High",
9 => "Emergency / Yellow Alert"
};
string label;
if (i == 1) label = "Lowest (only idle dupes)";
else if (i == 2) label = "Very Low";
else if (i == 3) label = "Low";
else if (i == 4) label = "Below Normal";
else if (i == 5) label = "Normal (default)";
else if (i == 6) label = "Above Normal";
else if (i == 7) label = "High";
else if (i == 8) label = "Very High";
else if (i == 9) label = "Emergency / Yellow Alert";
else label = "Normal";
levels.Add(new { priority = i, label, isYellowAlert = i == 9 });
}
return JsonSerializer.Serialize(levels);
@ -1848,24 +2030,54 @@ namespace ONIAgentBridge
foreach (var kv in dict) fb.data[kv.Key] = kv.Value;
}
// Auto-suggest for common errors
fb.suggestion = error switch
// Map error codes to suggestions
switch (error)
{
"cell_occupied" => "Choose a different location, or deconstruct the existing building first",
"cell_solid" => "Use dig action first to clear the area",
"cell_occupied_by_dupe" => "Wait for the duplicant to move, or cancel their current task",
"material_shortage" => "Check resource availability and produce or deliver the required material",
"cell_out_of_bounds" => "Stay within the playable area",
"unknown_building" => "Use 'registry buildings' to find valid building IDs",
"unknown_tech" => "Use 'registry techs' to find valid tech IDs",
"missing_prerequisites" => "Research the prerequisite technologies first",
"tech_already_complete" => "The technology has already been researched",
"no_building_at_cell" => "Use the buildings command to find buildings and their coordinates",
"no_liquid_at_cell" => "Use the cell command to check what is at that location",
"liquid_too_thin" => "Wait for more liquid to accumulate before mopping",
"invalid_priority" => "Use a value between 1 (lowest) and 9 (emergency/yellow alert)",
"blocks_not_diggable" => "Neutronium borders and abyssalite cannot be dug",
_ => null
};
case "cell_occupied":
fb.suggestion = "Choose a different location, or deconstruct the existing building first";
break;
case "cell_solid":
fb.suggestion = "Use dig action first to clear the area";
break;
case "cell_occupied_by_dupe":
fb.suggestion = "Wait for the duplicant to move, or cancel their current task";
break;
case "material_shortage":
fb.suggestion = "Check resource availability and produce or deliver the required material";
break;
case "cell_out_of_bounds":
fb.suggestion = "Stay within the playable area";
break;
case "unknown_building":
fb.suggestion = "Use 'registry buildings' to find valid building IDs";
break;
case "unknown_tech":
fb.suggestion = "Use 'registry techs' to find valid tech IDs";
break;
case "missing_prerequisites":
fb.suggestion = "Research the prerequisite technologies first";
break;
case "tech_already_complete":
fb.suggestion = "The technology has already been researched";
break;
case "no_building_at_cell":
fb.suggestion = "Use the buildings command to find buildings and their coordinates";
break;
case "no_liquid_at_cell":
fb.suggestion = "Use the cell command to check what is at that location";
break;
case "liquid_too_thin":
fb.suggestion = "Wait for more liquid to accumulate before mopping";
break;
case "invalid_priority":
fb.suggestion = "Use a value between 1 (lowest) and 9 (emergency/yellow alert)";
break;
case "blocks_not_diggable":
fb.suggestion = "Neutronium borders and abyssalite cannot be dug";
break;
default:
break;
}
return fb;
}
@ -3531,20 +3743,16 @@ namespace ONIAgentBridge
string wireType = data.type ?? "regular";
string mode = data.mode ?? "line";
string conduitId = wireType switch
{
"heavy" => "HeaviWatWire",
"conductive" => "ConductiveWire",
"heavy_conductive" => "HeaviWatConductiveWire",
_ => "Wire"
};
string bridgeId = wireType switch
{
"heavy" => "HeaviWatBridge",
"conductive" => "ConductiveWireBridge",
"heavy_conductive" => "HeaviWatConductiveBridge",
_ => "WireBridge"
};
string conduitId;
if (wireType == "heavy") conduitId = "HeaviWatWire";
else if (wireType == "conductive") conduitId = "ConductiveWire";
else if (wireType == "heavy_conductive") conduitId = "HeaviWatConductiveWire";
else conduitId = "Wire";
string bridgeId;
if (wireType == "heavy") bridgeId = "HeaviWatBridge";
else if (wireType == "conductive") bridgeId = "ConductiveWireBridge";
else if (wireType == "heavy_conductive") bridgeId = "HeaviWatConductiveBridge";
else bridgeId = "WireBridge";
int cellsPlaced = 0;
int bridgesPlaced = 0;

32
mod/ONIAgentBridge.csproj Normal file
View File

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<AssemblyName>ONIAgentBridge</AssemblyName>
<Version>1.0.0</Version>
<RootNamespace>ONIAgentBridge</RootNamespace>
<LangVersion>7</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.2.2" />
<PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<!-- Point these to your ONI installation directory -->
<Reference Include="Assembly-CSharp">
<HintPath>$(ONI_Path)/Assembly-CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Assembly-CSharp-firstpass">
<HintPath>$(ONI_Path)/Assembly-CSharp-firstpass.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(ONI_Path)/UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(ONI_Path)/UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
</Project>