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:
181
scripts/fix_csharp.py
Normal file
181
scripts/fix_csharp.py
Normal file
@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert C# 7/8 features in ONIAgentBridge.cs to C# 6 compatible code."""
|
||||
|
||||
import re
|
||||
|
||||
with open('mod/ONIAgentBridge.cs', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# 1. Convert tuple switch at ProcessRequest to if-else chain
|
||||
# Find the switch and replace it
|
||||
old_switch_start = """ switch (path, method)
|
||||
{"""
|
||||
|
||||
# Read the entire switch block and reconstruct
|
||||
lines = content.split('\n')
|
||||
new_lines = []
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
|
||||
# Find the C# 7 tuple switch
|
||||
if 'switch (path, method)' in line and '{' in lines[i+1]:
|
||||
new_lines.append(' // --- C# 6 compatible dispatch ---')
|
||||
new_lines.append(' bool _handled = false;')
|
||||
i += 2 # skip "switch (path, method)" and "{"
|
||||
|
||||
while i < len(lines):
|
||||
stripped = lines[i].strip()
|
||||
|
||||
# Check for case pattern: case ("...", "..."):
|
||||
m = re.match(r'case \("([^"]+)", "([^"]+)"\):', stripped)
|
||||
if m:
|
||||
endpoint = m.group(1)
|
||||
method = m.group(2)
|
||||
indent = lines[i][:len(lines[i]) - len(lines[i].lstrip())]
|
||||
new_lines.append(f'{indent}else if (path == "{endpoint}" && method == "{method}")')
|
||||
new_lines.append(f'{indent}{{')
|
||||
new_lines.append(f'{indent} _handled = true;')
|
||||
i += 1
|
||||
# Copy lines until "break;"
|
||||
while i < len(lines) and lines[i].strip() != 'break;':
|
||||
new_lines.append(lines[i])
|
||||
i += 1
|
||||
new_lines.append(f'{indent}}}')
|
||||
i += 1 # skip "break;"
|
||||
continue
|
||||
|
||||
# Check for default case
|
||||
if stripped == 'default:':
|
||||
i += 1
|
||||
# Skip the opening brace
|
||||
if lines[i].strip() == '{':
|
||||
i += 1
|
||||
new_lines.append(' if (!_handled)')
|
||||
new_lines.append(' {')
|
||||
while i < len(lines):
|
||||
if lines[i].strip() == '}' and not _is_end_of_method(lines, i):
|
||||
# This could be the switch closing brace
|
||||
# Check if next line is the method closing
|
||||
break
|
||||
new_lines.append(lines[i])
|
||||
i += 1
|
||||
new_lines.append(' }')
|
||||
# Skip the closing brace of the switch
|
||||
if lines[i].strip() == '}':
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Check for closing brace of switch
|
||||
if stripped == '}':
|
||||
i += 1
|
||||
break
|
||||
|
||||
# Any other content inside switch (shouldn't happen)
|
||||
new_lines.append(lines[i])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
new_lines.append(line)
|
||||
i += 1
|
||||
|
||||
content = '\n'.join(new_lines)
|
||||
|
||||
def _is_end_of_method(lines, idx):
|
||||
"""Check if this } ends a method (next non-blank is a method or class member)."""
|
||||
for j in range(idx+1, min(idx+10, len(lines))):
|
||||
s = lines[j].strip()
|
||||
if s == '' or s.startswith('//') or s.startswith('/*'):
|
||||
continue
|
||||
if s.startswith('private ') or s.startswith('public ') or s.startswith('internal ') or s.startswith('static ') or s.startswith('}'):
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
|
||||
# 2. Fix switch expressions (C# 8): `wireType switch { ... }` -> if-else
|
||||
# Pattern: string buildingId = wireType switch { "heavy" => "HeaviWatWire", ... }
|
||||
def fix_switch_expr(match):
|
||||
full = match.group(0)
|
||||
var_name = match.group(1)
|
||||
# Parse the switch arms
|
||||
# Extract the expression after "= "
|
||||
body = match.group(2)
|
||||
return full # placeholder, we'll handle below
|
||||
|
||||
# Fix: string buildingId = wireType switch { "heavy" => "HeaviWatWire", _ => "Wire" };
|
||||
# Convert to: string buildingId; if (wireType == "heavy") buildingId = "HeaviWatWire"; else ...
|
||||
for pattern in [
|
||||
(r'wireType switch\s*\{([^}]+)\}', 'wireType'),
|
||||
(r'error switch\s*\{([^}]+)\}', 'error'),
|
||||
(r'i switch\s*\{([^}]+)\}', 'i'),
|
||||
]:
|
||||
pat, varname = pattern
|
||||
matches = list(re.finditer(pat, content))
|
||||
for m in reversed(matches):
|
||||
body = m.group(1)
|
||||
arms = re.findall(r'"([^"]*)"\s*=>\s*"([^"]*)"', body)
|
||||
default = re.findall(r'_\s*=>\s*(null|"[^"]*")', body)
|
||||
|
||||
if varname == 'i':
|
||||
# label assignment
|
||||
replacements = []
|
||||
for key, val in arms:
|
||||
replacements.append(f' if ({varname} == {key}) label = "{val}";')
|
||||
if default:
|
||||
replacements.append(f' else label = {default[0]};')
|
||||
new_code = '\n'.join(replacements)
|
||||
else:
|
||||
# Find the variable being assigned
|
||||
start = m.start()
|
||||
# Look backwards to find the variable name
|
||||
line_start = content.rfind('\n', 0, start) + 1
|
||||
line_before = content[line_start:start]
|
||||
var_match = re.match(r'\s*(?:\w+\s+)?(\w+)\s*=\s*$', line_before)
|
||||
|
||||
indent = ' ' * 16
|
||||
if varname == 'wireType':
|
||||
# Multiple instances in wire building, find each by context
|
||||
context_before = content[max(0,start-200):start]
|
||||
if 'bridgeId' in context_before:
|
||||
varname_full = 'bridgeId'
|
||||
else:
|
||||
varname_full = 'buildingId'
|
||||
else:
|
||||
varname_full = varname
|
||||
|
||||
lines_code = [f' string {varname_full};']
|
||||
first = True
|
||||
for key, val in arms:
|
||||
prefix = 'if' if first else 'else if'
|
||||
lines_code.append(f' {prefix} ({varname} == "{key}") {varname_full} = "{val}";')
|
||||
first = False
|
||||
if default:
|
||||
dval = default[0]
|
||||
lines_code.append(f' else {varname_full} = {dval};')
|
||||
|
||||
new_code = '\n'.join(lines_code)
|
||||
|
||||
# Replace the assignment line + switch expression
|
||||
# Find the full assignment
|
||||
assign_start = content.rfind('\n', 0, start)
|
||||
assign_end = m.end()
|
||||
while assign_end < len(content) and content[assign_end] != '\n':
|
||||
assign_end += 1
|
||||
old = content[assign_start:assign_end]
|
||||
content = content[:assign_start] + '\n' + new_code + content[assign_end:]
|
||||
|
||||
# Write result
|
||||
with open('mod/ONIAgentBridge.cs', 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
print('Done. Checking for remaining C# 7+ features...')
|
||||
|
||||
# Verify
|
||||
remaining = re.findall(r'\bswitch\b', content)
|
||||
print(f'Remaining "switch" keywords: {len(remaining)}')
|
||||
# Count them
|
||||
for i, line in enumerate(content.split('\n')):
|
||||
if 'switch' in line and not line.strip().startswith('//') and 'ProcessRequest' not in line:
|
||||
sline = line.strip()
|
||||
if 'if (' not in line and 'dictionary' not in line.lower() and 'dispatch' not in line:
|
||||
print(f' Line ~{i}: {sline[:80]}')
|
||||
Reference in New Issue
Block a user