mirror of
https://github.com/riperiperi/FreeSO
synced 2026-08-13 20:26:13 -04:00
* Server connection back Get the server connection working again that was lost by the Archive mode changes. Key changes: - Fix server connection bugs caused by the new Archive mode - Update Microsoft.Data.Sqlite.Core to 10.0.2 - Add allOpenable config flag for free roam lot transitions for normal servers. - Add city and lot server configuration options - Make UCP.InitArchive public for CoreGameScreenController - Archive UI improvements (join dialog with direct server IP join) * Keyboard navigation Add full keyboard and Tab navigation support to the UI framework. Key changes: - Tab/Shift+Tab navigation between focusable UI elements - Keyboard support for UIButton, UISlider, UIListBox, UICombobox - UIRadioButton arrow key navigation within groups - Mouse wheel scrolling for UIListBox - UIContextMenu keyboard navigation (arrow keys, Enter, Escape) - UIGridViewer keyboard support - Focus management via InputManager with IFocusableUI interface * FSO.Unix cross-platform support Add FSO.Unix project for native Linux and macOS support. Key changes: - New FSO.Unix project with cross-platform entry point - macOS .app bundle creation and deploy script for macOS/Linux - Native dialogs via osascript (macOS) and zenity (Linux) - ImageSharp-based bitmap/PNG handling instead of Windows drawing library for non-Windows platforms - GameLocator path fixes for TSO on macOS and Linux * Docker containers Add Docker configuration files for running a FreeSO server. Key changes: - Dockerfile and docker-compose.yml for server deployment - Entrypoint script with secret generation - OCI image labels for container metadata - Default server configuration template (config.json) * Spectator mode Allow non-roommate visitors to open and explore lots in a read-only spectator mode. Spectators can walk around and observe but cannot modify the lot (build, buy, delete objects, or use most interactions). Key features: - Basic spectator mode for non-roommate lot opening - Block VM commands, cross-room routing, and portal interactions for spectators - Block pie menu on lot objects with error feedback - Spectator-to-writable transition when a roommate/owner joins - Reload lot save and transition back when lot owner joins - Walls up with roof on spectator lot entry - Spectator label shown in UI - Admins excluded from spectator mode restrictions - Chat event when lot transitions from spectator mode - Allow spectators to use direct control - No spectator lots in Archive mode Also includes: cursor fixes for macOS/Linux, PriorityQueue pathfinder optimization, UIListBox focus fix, CurLoader BMP header fix, API config cleanup, and various other minor fixes. * Spector mode improvements
137 lines
4.5 KiB
C#
137 lines
4.5 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using FSO.Client;
|
|
using FSO.Client.UI.Panels;
|
|
using FSO.Common;
|
|
using SixLabors.ImageSharp;
|
|
using SixLabors.ImageSharp.PixelFormats;
|
|
using SixLabors.ImageSharp.Formats.Png;
|
|
|
|
namespace FSO.Unix
|
|
{
|
|
public static class Program
|
|
{
|
|
/// <summary>
|
|
/// The main entry point for the application.
|
|
/// </summary>
|
|
public static void Main(string[] args)
|
|
{
|
|
InitUnix();
|
|
|
|
var mgAssembly = typeof(Microsoft.Xna.Framework.Game).Assembly;
|
|
var platform = mgAssembly.GetType("MonoGame.Framework.Utilities.PlatformInfo");
|
|
var backend = platform?.GetProperty("GraphicsBackend")?.GetValue(null);
|
|
Console.WriteLine($"[FreeSO] MonoGame: {mgAssembly.GetName().Version} | Backend: {backend ?? "Unknown"}");
|
|
|
|
FSOEnvironment.Enable3D = true;
|
|
|
|
if ((new FSOProgram()).InitWithArguments(args))
|
|
{
|
|
var startProxy = new GameStartProxy();
|
|
startProxy.Start(false);
|
|
}
|
|
|
|
Environment.Exit(0);
|
|
}
|
|
|
|
public static void InitUnix()
|
|
{
|
|
FSO.Files.ImageLoaderHelpers.BitmapFunction = BitmapReader;
|
|
FSO.Files.ImageLoaderHelpers.SavePNGFunc = SavePNG;
|
|
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
|
FSOProgram.ShowDialog = ShowDialog;
|
|
}
|
|
|
|
public static void ShowDialog(string text)
|
|
{
|
|
ShowDialog(text, "FreeSO Message");
|
|
}
|
|
|
|
private static string Escape(string s) => s.Replace("\"", "\\\"");
|
|
|
|
private static void ShowDialog(string text, string title)
|
|
{
|
|
if (text.Length > 1500) text = text.Substring(0, 1500) + "...";
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = "osascript",
|
|
Arguments = $"-e \"display alert \\\"{Escape(title)}\\\" message \\\"{Escape(text)}\\\" giving up after 15\"",
|
|
UseShellExecute = true
|
|
};
|
|
Process.Start(psi)?.WaitForExit();
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = "zenity",
|
|
Arguments = $"--error --title=\"{Escape(title)}\" --text=\"{Escape(text)}\"",
|
|
UseShellExecute = false
|
|
};
|
|
Process.Start(psi)?.WaitForExit();
|
|
}
|
|
catch
|
|
{
|
|
Console.Error.WriteLine($"[{title}] {text}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
{
|
|
string title = e.ExceptionObject is OutOfMemoryException
|
|
? "Out of Memory! FreeSO needs to close."
|
|
: "A fatal error occured! Screenshot this dialog and post it on Discord.";
|
|
|
|
ShowDialog(e.ExceptionObject.ToString(), title);
|
|
Environment.Exit(1);
|
|
}
|
|
|
|
public static void SavePNG(byte[] data, int width, int height, Stream str)
|
|
{
|
|
using var image = new Image<Rgba32>(width, height);
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
int i = (y * width + x) * 4;
|
|
image[x, y] = new Rgba32(data[i], data[i + 1], data[i + 2], data[i + 3]);
|
|
}
|
|
}
|
|
|
|
image.Save(str, new PngEncoder());
|
|
}
|
|
|
|
public static Tuple<byte[], int, int> BitmapReader(Stream str)
|
|
{
|
|
using var image = Image.Load<Rgba32>(str);
|
|
int width = image.Width;
|
|
int height = image.Height;
|
|
|
|
var data = new byte[width * height * 4];
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
int i = (y * width + x) * 4;
|
|
Rgba32 px = image[x, y];
|
|
data[i] = px.R;
|
|
data[i + 1] = px.G;
|
|
data[i + 2] = px.B;
|
|
data[i + 3] = px.A;
|
|
}
|
|
}
|
|
|
|
return new Tuple<byte[], int, int>(data, width, height);
|
|
}
|
|
}
|
|
}
|