freeso/TSOClient/FSOFacadeWorker/Program.cs
riperiperi b57ebbd141 Cleanup: Remove most unused usings and MPL per-file licenses
MPL per-file was used in older project files, but should apply to the whole project, so having it there was redundant.
2023-11-25 03:45:29 +00:00

394 lines
15 KiB
C#

using FSO.Client.Utils.GameLocator;
using FSO.Common;
using FSO.Common.Rendering.Framework;
using FSO.Common.Utils;
using FSO.Content;
using FSO.Files.RC;
using FSO.LotView;
using FSO.LotView.Facade;
using FSO.Server.Clients;
using FSO.SimAntics;
using FSO.SimAntics.Engine;
using FSO.SimAntics.Engine.TSOTransaction;
using FSO.SimAntics.Marshals;
using FSO.SimAntics.Model;
using FSO.SimAntics.NetPlay.Drivers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
namespace FSOFacadeWorker
{
class Program
{
private static _3DLayer Layer;
private static GraphicsDevice GD;
private static FacadeConfig Config;
private static uint DebugLot;
static void Main(string[] args)
{
FSO.Windows.Program.InitWindows();
TimedReferenceController.SetMode(CacheType.PERMANENT);
Console.WriteLine("Loading Config...");
try
{
var configString = File.ReadAllText("facadeconfig.json");
Config = Newtonsoft.Json.JsonConvert.DeserializeObject<FacadeConfig>(configString);
}
catch (Exception e)
{
Console.WriteLine("Could not find configuration file 'facadeconfig.json'. Please ensure it is valid and present in the same folder as this executable.");
return;
}
Console.WriteLine("Locating The Sims Online...");
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
Directory.SetCurrentDirectory(baseDir);
//Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
OperatingSystem os = Environment.OSVersion;
PlatformID pid = os.Platform;
ILocator gameLocator;
bool linux = pid == PlatformID.MacOSX || pid == PlatformID.Unix;
if (linux && Directory.Exists("/Users"))
gameLocator = new MacOSLocator();
else if (linux)
gameLocator = new LinuxLocator();
else
gameLocator = new WindowsLocator();
bool useDX = true;
FSOEnvironment.Enable3D = true;
GraphicsModeControl.ChangeMode(FSO.LotView.Model.GlobalGraphicsMode.Full3D);
GameThread.NoGame = true;
GameThread.UpdateExecuting = true;
var path = Config.Game_Path ?? gameLocator.FindTheSimsOnline();
if (path != null)
{
FSOEnvironment.ContentDir = "Content/";
FSOEnvironment.GFXContentDir = "Content/" + (useDX ? "DX/" : "OGL/");
FSOEnvironment.Linux = linux;
FSOEnvironment.DirectX = useDX;
FSOEnvironment.TexCompress = FSOEnvironment.TexCompressSupport;
FSOEnvironment.GameThread = Thread.CurrentThread;
FSO.HIT.HITVM.Init();
FSO.HIT.HITVM.Get().SetMasterVolume(FSO.HIT.Model.HITVolumeGroup.AMBIENCE, 0);
FSO.HIT.HITVM.Get().SetMasterVolume(FSO.HIT.Model.HITVolumeGroup.FX, 0);
FSO.HIT.HITVM.Get().SetMasterVolume(FSO.HIT.Model.HITVolumeGroup.MUSIC, 0);
FSO.HIT.HITVM.Get().SetMasterVolume(FSO.HIT.Model.HITVolumeGroup.VOX, 0);
FSO.Files.Formats.IFF.Chunks.STR.DefaultLangCode = FSO.Files.Formats.IFF.Chunks.STRLangCode.EnglishUS;
}
Console.WriteLine("Creating Graphics Device...");
var gds = new GraphicsDeviceServiceMock();
var gd = gds.GraphicsDevice;
//set up some extra stuff like the content manager
var services = new GameServiceContainer();
var content = new ContentManager(services);
content.RootDirectory = FSOEnvironment.GFXContentDir;
services.AddService<IGraphicsDeviceService>(gds);
var vitaboyEffect = content.Load<Effect>("Effects/Vitaboy");
FSO.Vitaboy.Avatar.setVitaboyEffect(vitaboyEffect);
WorldConfig.Current = new WorldConfig()
{
LightingMode = 3,
SmoothZoom = true,
SurroundingLots = 0
};
DGRP3DMesh.Sync = true;
Console.WriteLine("Looks like that worked. Loading FSO Content!");
VMContext.InitVMConfig(false);
Content.Init(path, gd);
WorldContent.Init(services, content.RootDirectory);
VMAmbientSound.ForceDisable = true;
Layer = new _3DLayer();
Layer.Initialize(gd);
GD = gd;
if (args.FirstOrDefault() == "debug")
{
DebugLot = uint.Parse(args[1]);
}
Console.WriteLine("Starting Worker Loop!");
WorkerLoop();
Console.WriteLine("Exiting.");
GameThread.SetKilled();
gds.Release();
}
public static ApiClient api;
public static int TotalLotNum = 0;
public static List<uint> LotQueue = new List<uint>();
private static bool LoginSent;
private static int Done;
public static void Login()
{
api = new ApiClient(Config.Api_Url);
api.AdminLogin(Config.User, Config.Password, (result) =>
{
if (!result)
{
Console.WriteLine("Login Failed! Trying again in a wee bit.");
LoginSent = false;
}
else
{
if (DebugLot != 0)
{
RenderStandaloneDebug(1, DebugLot);
}
else
{
api.GetLotList(1, (lots) =>
{
Console.WriteLine("Got a lot list for full thumbnail rebake.");
//LotQueue.AddRange(lots);
//TotalLotNum += lots.Length;
//for (int i = 0; i < 4000; i++)
//{
// LotQueue.RemoveAt(0);
//}
RenderLot();
RenderLot();
});
}
}
});
}
private static void SaveRawImage(byte[] data, int width, int height, string path)
{
for (int i = 0; i < data.Length; i += 4)
{
var temp = data[i];
data[i] = data[i + 2];
data[i + 2] = temp;
}
var image = Image.LoadPixelData<Rgba32>(data, width, height);
using (var stream = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None)) {
image.SaveAsPng(stream);
}
}
private static void RenderStandaloneDebug(uint shard, uint location)
{
Console.WriteLine("===== Trying standalone render for " + location + "! =====");
api.GetFSOV((uint)shard, location, (bt) =>
{
try
{
if (bt == null)
{
Console.WriteLine("===== Could not find lot " + location + "! =====");
}
else
{
var fsof = RenderFSOF(bt, GD, false);
Directory.CreateDirectory("test/");
using (var mem = new MemoryStream())
{
fsof.Save(mem);
File.WriteAllBytes("test/" + location + ".fsof", mem.ToArray());
}
//save the images
SaveRawImage(fsof.FloorTextureData, fsof.FloorWidth, fsof.FloorHeight, "test/" + location + "_floor.png");
SaveRawImage(fsof.WallTextureData, fsof.WallWidth, fsof.WallHeight, "test/" + location + "_wall.png");
SaveRawImage(fsof.NightFloorTextureData, fsof.FloorWidth, fsof.FloorHeight, "test/" + location + "_nfloor.png");
SaveRawImage(fsof.NightWallTextureData, fsof.WallWidth, fsof.WallHeight, "test/" + location + "n_wall.png");
Console.WriteLine("===== Done! =====");
}
}
catch (IOException e)
{
Console.WriteLine("===== Could not render lot " + location + "! =====");
Console.WriteLine(e.ToString());
}
});
}
private static void RenderLot()
{
GD.Present();
Console.WriteLine("Requesting work...");
api.GetWork((shard, location) =>
{
if (shard == -1)
{
//no work
if (location == uint.MaxValue)
{
//error, try logging in again
LoginSent = false;
GameThread.OnWork.Set();
return;
} else
{
if (Config.Sleep_Time == 0)
{
//exit when no work remains
Environment.Exit(0);
return;
}
//no work, try again in 30 s
Thread.Sleep(Config.Sleep_Time);
RenderLot();
return;
}
}
api.GetFSOV((uint)shard, location, (bt) =>
{
try
{
if (bt == null)
{
RenderLot();
}
else
{
Console.WriteLine("Rendering lot " + location + "...");
var fsof = RenderFSOF(bt, GD, true);
using (var mem = new MemoryStream())
{
fsof.Save(mem);
Console.WriteLine("Done! Uploading FSOF for lot " + location + ".");
//File.WriteAllBytes("C:/fsof/" + lot + ".fsof", mem.ToArray());
//RenderLot();
api.UploadFSOF(1, location, mem.ToArray(), (success) =>
{
if (!success)
{
Console.WriteLine("Uploading fsof for " + location + " did not succeed.");
}
if (Done++ > Config.Limit)
{
Console.WriteLine("Restarting due to large number of thumbnails rendered.");
Environment.Exit(0);
return;
}
RenderLot();
});
}
}
}
catch (IOException e)
{
Console.WriteLine("===== Could not render lot " + location + "! =====");
Console.WriteLine(e.ToString());
RenderLot();
}
});
});
}
public static void WorkerLoop()
{
int loggedIn = 0;
int loginAttempts = 0;
while (true)
{
if (!LoginSent)
{
LoginSent = true;
Console.WriteLine("Attempting Login... ("+(loginAttempts++)+")");
Login();
}
GameThread.OnWork.WaitOne(1000);
GameThread.DigestUpdate(null);
}
}
public static FSOF RenderFSOF(byte[] fsov, GraphicsDevice gd, bool compressed)
{
var marshal = new VMMarshal();
using (var mem = new MemoryStream(fsov))
{
marshal.Deserialize(new BinaryReader(mem));
}
var world = new World(gd);
world.Opacity = 1;
Layer.Add(world);
var globalLink = new VMTSOGlobalLinkStub();
var driver = new VMServerDriver(globalLink);
var vm = new VM(new VMContext(world), driver, new VMNullHeadlineProvider());
vm.Init();
vm.Load(marshal);
SetOutsideTime(gd, vm, world, 0.5f, false);
world.State.PrepareLighting();
var facade = new LotFacadeGenerator();
facade.FLOOR_TILES = 64;
facade.GROUND_SUBDIV = 5;
facade.FLOOR_RES_PER_TILE = 2;
SetAllLights(vm, world, 0.5f, 0);
var result = facade.GetFSOF(gd, world, vm.Context.Blueprint, () => { SetAllLights(vm, world, 0.0f, 100); }, compressed);
Layer.Remove(world);
world.Dispose();
vm.Context.Ambience.Kill();
foreach (var ent in vm.Entities)
{ //stop object sounds
var threads = ent.SoundThreads;
for (int i = 0; i < threads.Count; i++)
{
threads[i].Sound.RemoveOwner(ent.ObjectID);
}
threads.Clear();
}
return result;
}
private static void SetAllLights(VM vm, World world, float outsideTime, short contribution)
{
foreach (var light in vm.Entities.Where(x => x.Object.Resource.SemiGlobal?.Iff?.Filename == "lightglobals.iff"))
{
light.SetValue(FSO.SimAntics.Model.VMStackObjectVariable.LightingContribution, contribution);
}
vm.Context.Architecture.SignalAllDirty();
vm.Context.Architecture.Tick();
SetOutsideTime(GD, vm, world, outsideTime, false);
}
private static void SetOutsideTime(GraphicsDevice gd, VM vm, World world, float time, bool lightsOn)
{
vm.Context.Architecture.SetTimeOfDay(time);
world.Force2DPredraw(gd);
vm.Context.Architecture.SetTimeOfDay();
}
}
}