Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 92 additions & 36 deletions VisualPinball.Engine.PinMAME.Unity/Runtime/PinMameGamelogicEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,14 @@ public GamelogicEngineSwitch[] RequestedSwitches {
return _game?.AvailableSwitches ?? Array.Empty<GamelogicEngineSwitch>();
}
}

public GamelogicEngineCoil[] RequestedCoils {
get {
UpdateCaches();
return _coils.Values.ToArray();
}
}

public GamelogicEngineLamp[] RequestedLamps {
get {
UpdateCaches();
Expand All @@ -101,8 +103,15 @@ public GamelogicEngineLamp[] RequestedLamps {
[SerializeReference] private PinMameGame _game;

private Dictionary<string, GamelogicEngineSwitch> _switches = new();
private Dictionary<int, GamelogicEngineCoil> _coils = new();
private Dictionary<int, GamelogicEngineLamp> _lamps = new();
private Dictionary<int, string> _pinMameIdToSwitchIdMappings = new();
private Dictionary<string, int> _switchIdToPinMameIdMappings = new();

private Dictionary<string, GamelogicEngineCoil> _coils = new();
private Dictionary<int, string> _pinMameIdToCoilIdMapping = new();
private Dictionary<string, int> _coilIdToPinMameIdMapping = new();

private Dictionary<string, GamelogicEngineLamp> _lamps = new();
private Dictionary<int, string> _pinMameIdToLampIdMapping = new();

private bool _isRunning;
private int _numMechs;
Expand Down Expand Up @@ -164,17 +173,17 @@ private void Update()

// lamps
foreach (var changedLamp in _pinMame.GetChangedLamps()) {
if (_lamps.ContainsKey(changedLamp.Id)) {
if (_pinMameIdToLampIdMapping.ContainsKey(changedLamp.Id)) {
//Logger.Info($"[PinMAME] <= lamp {changedLamp.Id}: {changedLamp.Value}");
OnLampChanged?.Invoke(this, new LampEventArgs(_lamps[changedLamp.Id].Id, changedLamp.Id, changedLamp.Value));
OnLampChanged?.Invoke(this, new LampEventArgs(_lamps[_pinMameIdToLampIdMapping[changedLamp.Id]].Id, changedLamp.Value));
}
}

// gi
foreach (var changedGi in _pinMame.GetChangedGIs()) {
if (_lamps.ContainsKey(changedGi.Id)) {
if (_pinMameIdToLampIdMapping.ContainsKey(changedGi.Id)) {
//Logger.Info($"[PinMAME] <= gi {changedGi.Id}: {changedGi.Value}");
OnLampChanged?.Invoke(this, new LampEventArgs(_lamps[changedGi.Id].Id, _lamps[changedGi.Id].InternalId, changedGi.Value, LampSource.GI));
OnLampChanged?.Invoke(this, new LampEventArgs(_lamps[_pinMameIdToLampIdMapping[changedGi.Id]].Id, changedGi.Value, LampSource.GI));
} else {
Debug.Log($"No GI {changedGi.Id} found.");
}
Expand Down Expand Up @@ -279,11 +288,17 @@ private void OnGameStarted()
Logger.Info($"[PinMAME] Game started.");
_isRunning = true;

SendInitialSwitches();
SendMechs();

_solenoidDelayStart = DateTimeOffset.Now.ToUnixTimeMilliseconds();

try {
SendInitialSwitches();
SendMechs();
}

catch(Exception e) {
Logger.Error($"[PinMAME] OnGameStarted: {e.Message}");
}

lock (_dispatchQueue) {
_dispatchQueue.Enqueue(() => OnStarted?.Invoke(this, EventArgs.Empty));
}
Expand All @@ -300,17 +315,61 @@ private void UpdateCaches()
if (_game == null) {
return;
}

_lamps.Clear();
_pinMameIdToLampIdMapping.Clear();

_coils.Clear();
_pinMameIdToCoilIdMapping.Clear();

_switches.Clear();
foreach (var lamp in _game.AvailableLamps) {
_lamps[lamp.InternalId] = lamp;
_pinMameIdToSwitchIdMappings.Clear();
_switchIdToPinMameIdMappings.Clear();

foreach (var alias in _game.AvailableAliases) {
switch (alias.AliasType) {
case AliasType.Switch:
_pinMameIdToSwitchIdMappings[alias.Id] = alias.Name;
_switchIdToPinMameIdMappings[alias.Name] = alias.Id;
break;


case AliasType.Coil:
_pinMameIdToCoilIdMapping[alias.Id] = alias.Name;
_coilIdToPinMameIdMapping[alias.Name] = alias.Id;
break;

case AliasType.Lamp:
_pinMameIdToLampIdMapping[alias.Id] = alias.Name;
break;
}
}


foreach (var @switch in _game.AvailableSwitches) {
_switches[@switch.Id] = @switch;

if (int.TryParse(@switch.Id, out int pinMameId)) {
_pinMameIdToSwitchIdMappings[pinMameId] = @switch.Id;
_switchIdToPinMameIdMappings[@switch.Id] = pinMameId;
}
}

foreach (var coil in _game.AvailableCoils) {
_coils[coil.InternalId] = coil;
_coils[coil.Id] = coil;

if (int.TryParse(coil.Id, out int pinMameId)) {
_pinMameIdToCoilIdMapping[pinMameId] = coil.Id;
_coilIdToPinMameIdMapping[coil.Id] = pinMameId;
}
}
foreach (var sw in _game.AvailableSwitches) {
_switches[sw.Id] = sw;

foreach (var lamp in _game.AvailableLamps) {
_lamps[lamp.Id] = lamp;

if (int.TryParse(lamp.Id, out int pinMameId)) {
_pinMameIdToLampIdMapping[pinMameId] = lamp.Id;
}
}
}

Expand Down Expand Up @@ -568,15 +627,15 @@ public void SetCoil(string n, bool value)
{
OnCoilChanged?.Invoke(this, new CoilEventArgs(n, value));
}

public bool GetCoil(string id)
{
return _player != null && _player.CoilStatuses.ContainsKey(id) && _player.CoilStatuses[id];
}

private void OnSolenoidUpdated(int internalId, bool isActive)
private void OnSolenoidUpdated(int id, bool isActive)
{
if (_coils.ContainsKey(internalId)) {

if (_pinMameIdToCoilIdMapping.ContainsKey(id)) {
if (!_solenoidsEnabled) {
_solenoidsEnabled = DateTimeOffset.Now.ToUnixTimeMilliseconds() - _solenoidDelayStart >= SolenoidDelay;

Expand All @@ -585,25 +644,21 @@ private void OnSolenoidUpdated(int internalId, bool isActive)
}
}

var coil = _coils[_pinMameIdToCoilIdMapping[id]];

if (_solenoidsEnabled) {
var coil = _coils[internalId];
var id = coil != null ? coil.Id : internalId.ToString();
var desc = coil != null ? coil.Description : "-";
Logger.Info($"[PinMAME] <= coil {id} ({internalId}): {isActive} | {desc}");
Logger.Info($"[PinMAME] <= coil {coil.Id} : {isActive} | {coil.Description}");

lock (_dispatchQueue) {
id = _coils[internalId].Id;
_dispatchQueue.Enqueue(() => OnCoilChanged?.Invoke(this, new CoilEventArgs(id, isActive)));
_dispatchQueue.Enqueue(() => OnCoilChanged?.Invoke(this, new CoilEventArgs(coil.Id, isActive)));
}
}
else
{
Logger.Info($"[PinMAME] <= solenoids disabled, coil {_coils[internalId].Id} ({internalId}): {isActive} | {_coils[internalId].Description}");
else {
Logger.Info($"[PinMAME] <= solenoids disabled, coil {coil.Id} : {isActive} | {coil.Description}");
}
}
else
{
Logger.Warn($"[PinMAME] <= coil UNMAPPED {internalId}: {isActive}");
else {
Logger.Warn($"[PinMAME] <= coil UNMAPPED {id}: {isActive}");
}
}

Expand Down Expand Up @@ -635,22 +690,23 @@ public void SendInitialSwitches()
if (!isClosed) {
continue;
}
if (_switches.ContainsKey(id) && !_mechSwitches.Contains(_switches[id].InternalId)) {
Logger.Info($"[PinMAME] => sw {id} ({_switches[id].InternalId}): {true} | {_switches[id].Description}");
_pinMame.SetSwitch(_switches[id].InternalId, true);
if (_switches.ContainsKey(id) && !_mechSwitches.Contains(_switchIdToPinMameIdMappings[_switches[id].Id])) {
Logger.Info($"[PinMAME] => sw {id} ({_switches[id].Id}): {true} | {_switches[id].Description}");

_pinMame.SetSwitch(_switchIdToPinMameIdMappings[_switches[id].Id], true);
}
}
}

public void Switch(string id, bool isClosed)
{
if (_switches.ContainsKey(id)) {
if (_mechSwitches.Contains(_switches[id].InternalId)) {
if (_mechSwitches.Contains(_switchIdToPinMameIdMappings[_switches[id].Id])) {
// mech switches are triggered internally by pinmame.
return;
}
Logger.Info($"[PinMAME] => sw {id} ({_switches[id].InternalId}): {isClosed} | {_switches[id].Description}");
_pinMame.SetSwitch(_switches[id].InternalId, isClosed);
Logger.Info($"[PinMAME] => sw {id}: {isClosed} | {_switches[id].Description}");
_pinMame.SetSwitch(_switchIdToPinMameIdMappings[_switches[id].Id], isClosed);
} else {
Logger.Error($"[PinMAME] Unknown switch \"{id}\".");
}
Expand Down Expand Up @@ -698,7 +754,7 @@ private void SendMechs()
Logger.Error($"PinMAME only supports up to {max} custom mechs, ignoring {mech.name}.");
return;
}
var mechConfig = mech.Config(_player.SwitchMapping, _player.CoilMapping);
var mechConfig = mech.Config(_player.SwitchMapping, _player.CoilMapping, _switchIdToPinMameIdMappings, _coilIdToPinMameIdMapping);
_pinMame.SetMech(id, mechConfig);
foreach (var c in mechConfig.SwitchList) {
_mechSwitches.Add(c.SwNo);
Expand Down
24 changes: 17 additions & 7 deletions VisualPinball.Engine.PinMAME.Unity/Runtime/PinMameMechComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class PinMameMechComponent : MonoBehaviour, IMechHandler, ISwitchDeviceCo

public event EventHandler<MechEventArgs> OnMechUpdate;

public PinMameMechConfig Config(List<SwitchMapping> switchMappings, List<CoilMapping> coilMappings)
public PinMameMechConfig Config(List<SwitchMapping> switchMappings, List<CoilMapping> coilMappings, Dictionary<string, int> switchIdToPinMameIdMappings, Dictionary<string, int> coilIdToPinMameIdMappings)
{
var type = 0u;
type |= Type switch {
Expand All @@ -102,13 +102,23 @@ public PinMameMechConfig Config(List<SwitchMapping> switchMappings, List<CoilMap
type |= FastUpdates ? (uint)PinMameMechFlag.Fast : (uint)PinMameMechFlag.Slow;
type |= ResultByLength ? (uint)PinMameMechFlag.LengthSw : (uint)PinMameMechFlag.StepSw;

int coilId1 = 0;
int coilId2 = 0;

var coilMapping1 = coilMappings.FirstOrDefault(cm => ReferenceEquals(cm.Device, this) && cm.DeviceItem == _solenoid1);
if (coilMapping1 != null && coilIdToPinMameIdMappings.ContainsKey(coilMapping1.Id)) {
coilId1 = coilIdToPinMameIdMappings[coilMapping1.Id];
}

var coilMapping2 = coilMappings.FirstOrDefault(cm => ReferenceEquals(cm.Device, this) && cm.DeviceItem == _solenoid2);
if (coilMapping2 != null && coilIdToPinMameIdMappings.ContainsKey(coilMapping2.Id)) {
coilId2 = coilIdToPinMameIdMappings[coilMapping2.Id];
}

var mechConfig = new PinMameMechConfig(
type,
coilMapping1?.InternalId ?? 0,
coilMapping2?.InternalId ?? 0,
coilId1,
coilId2,
Length,
Steps,
0,
Expand All @@ -125,16 +135,17 @@ public PinMameMechConfig Config(List<SwitchMapping> switchMappings, List<CoilMap

switch (mark.Type) {
case MechMarkSwitchType.EnableBetween:
mechConfig.AddSwitch(new PinMameMechSwitchConfig(switchMapping.InternalId, mark.StepBeginning, mark.StepEnd));
mechConfig.AddSwitch(new PinMameMechSwitchConfig(switchIdToPinMameIdMappings[switchMapping.Id], mark.StepBeginning, mark.StepEnd));
break;

case MechMarkSwitchType.AlwaysPulse:
mechConfig.AddSwitch(new PinMameMechSwitchConfig(switchMapping.InternalId, -mark.PulseFreq, mark.PulseDuration));
mechConfig.AddSwitch(new PinMameMechSwitchConfig(switchIdToPinMameIdMappings[switchMapping.Id], -mark.PulseFreq, mark.PulseDuration));
break;

case MechMarkSwitchType.PulseBetween:
mechConfig.AddSwitch(new PinMameMechSwitchConfig(switchMapping.InternalId, mark.StepBeginning, mark.StepEnd, mark.PulseFreq));
mechConfig.AddSwitch(new PinMameMechSwitchConfig(switchIdToPinMameIdMappings[switchMapping.Id], mark.StepBeginning, mark.StepEnd, mark.PulseFreq));
break;

default:
throw new ArgumentOutOfRangeException();
}
Expand Down Expand Up @@ -231,7 +242,6 @@ public void OnBeforeSerialize()
_solenoid2 = GenerateCoilId();
}


var switchIds = new HashSet<string>();
foreach (var mark in Marks) {
if (!mark.HasId || switchIds.Contains(mark.SwitchId)) {
Expand Down
Loading