Merge pull request 'master' (#67) from Alienjack/AxibugEmuOnline:master into master
Reviewed-on: #67
This commit is contained in:
commit
417390227c
2
.gitignore
vendored
2
.gitignore
vendored
@ -18,3 +18,5 @@
|
|||||||
/AxibugEmuOnline.Server/config.cfg
|
/AxibugEmuOnline.Server/config.cfg
|
||||||
/AxibugEmuOnline.Server/bin/
|
/AxibugEmuOnline.Server/bin/
|
||||||
/AxibugEmuOnline.Client/.editorconfig
|
/AxibugEmuOnline.Client/.editorconfig
|
||||||
|
/AxibugEmuOnline.Client/*.user
|
||||||
|
/AxibugEmuOnline.Client/.idea
|
||||||
|
@ -63,5 +63,10 @@
|
|||||||
/// 服务器等待Step更新
|
/// 服务器等待Step更新
|
||||||
/// </summary>
|
/// </summary>
|
||||||
OnRoomWaitStepChange,
|
OnRoomWaitStepChange,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当房间中手柄位信息发生任何变化时触发,进入房间后也应该触发
|
||||||
|
/// </summary>
|
||||||
|
OnRoomSlotDataChanged, //todo : 实现这个事件
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using AxibugEmuOnline.Client.ClientCore;
|
using AxibugEmuOnline.Client.ClientCore;
|
||||||
using AxibugEmuOnline.Client.Event;
|
using AxibugEmuOnline.Client.Event;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using VirtualNes.Core;
|
||||||
|
|
||||||
namespace AxibugEmuOnline.Client.Manager
|
namespace AxibugEmuOnline.Client.Manager
|
||||||
{
|
{
|
||||||
@ -11,6 +12,9 @@ namespace AxibugEmuOnline.Client.Manager
|
|||||||
/// 但是Equals方法可以,所以,这个接口判断为空请使用Equals
|
/// 但是Equals方法可以,所以,这个接口判断为空请使用Equals
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IEmuCore m_emuCore;
|
private IEmuCore m_emuCore;
|
||||||
|
|
||||||
|
private IControllerSetuper m_controllerSetuper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// unity的c#实现有bug,以接口类型保存的monobehaviour引用,!=和==运算符没有调用到monobehaviour重写过的运算符
|
/// unity的c#实现有bug,以接口类型保存的monobehaviour引用,!=和==运算符没有调用到monobehaviour重写过的运算符
|
||||||
/// 但是Equals方法可以,所以,这个接口判断为空请使用Equals
|
/// 但是Equals方法可以,所以,这个接口判断为空请使用Equals
|
||||||
@ -28,7 +32,7 @@ namespace AxibugEmuOnline.Client.Manager
|
|||||||
if (!m_emuCore.IsNull()) StopGame();
|
if (!m_emuCore.IsNull()) StopGame();
|
||||||
|
|
||||||
var roomInfo = App.roomMgr.mineRoomMiniInfo;
|
var roomInfo = App.roomMgr.mineRoomMiniInfo;
|
||||||
roomInfo.FetchRomFileInRoomInfo(EnumPlatform.NES, (room, romFile) =>
|
roomInfo.FetchRomFileInRoomInfo(EnumPlatform.NES, (_, romFile) =>
|
||||||
{
|
{
|
||||||
if (!romFile.RomReady) //这个rom并没有下载,所以取消进入房间
|
if (!romFile.RomReady) //这个rom并没有下载,所以取消进入房间
|
||||||
{
|
{
|
||||||
@ -58,6 +62,35 @@ namespace AxibugEmuOnline.Client.Manager
|
|||||||
InGameUI.Instance.Show(romFile, m_emuCore);
|
InGameUI.Instance.Show(romFile, m_emuCore);
|
||||||
|
|
||||||
m_emuCore.SetupScheme();
|
m_emuCore.SetupScheme();
|
||||||
|
|
||||||
|
m_controllerSetuper = Supporter.GetControllerSetuper();
|
||||||
|
|
||||||
|
SetupController();
|
||||||
|
|
||||||
|
Eventer.Instance.RegisterEvent(EEvent.OnRoomSlotDataChanged,OnSlotDataChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSlotDataChanged()
|
||||||
|
{
|
||||||
|
SetupController();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupController()
|
||||||
|
{
|
||||||
|
if (!App.roomMgr.InRoom) //不在房间中,自动分配0号手柄到0号手柄位
|
||||||
|
{
|
||||||
|
m_controllerSetuper.SetConnect(con0ToSlot: 0);
|
||||||
|
}
|
||||||
|
else //在房间中则使用服务器下发的手柄槽位信息分配本地手柄
|
||||||
|
{
|
||||||
|
long selfUID = App.user.userdata.UID;
|
||||||
|
App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 0, out var con0Slot);
|
||||||
|
App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 1, out var con1Slot);
|
||||||
|
App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 2, out var con2Slot);
|
||||||
|
App.roomMgr.mineRoomMiniInfo.GetPlayerSlotIdxByUid(selfUID, 3, out var con3Slot);
|
||||||
|
|
||||||
|
m_controllerSetuper.SetConnect(con0Slot, con1Slot, con2Slot, con3Slot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopGame()
|
public void StopGame()
|
||||||
@ -68,6 +101,8 @@ namespace AxibugEmuOnline.Client.Manager
|
|||||||
|
|
||||||
InGameUI.Instance.Hide();
|
InGameUI.Instance.Hide();
|
||||||
LaunchUI.Instance.ShowMainMenu();
|
LaunchUI.Instance.ShowMainMenu();
|
||||||
|
m_controllerSetuper = null;
|
||||||
|
Eventer.Instance.UnregisterEvent(EEvent.OnRoomSlotDataChanged,OnSlotDataChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetGame()
|
public void ResetGame()
|
||||||
|
@ -492,6 +492,26 @@ namespace AxibugEmuOnline.Client.Manager
|
|||||||
return freeSlots.Length > 0;
|
return freeSlots.Length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指定uid和该uid的本地手柄序号,获取占用的手柄位
|
||||||
|
/// </summary>
|
||||||
|
public static bool GetPlayerSlotIdxByUid(this Protobuf_Room_MiniInfo roomMiniInfo, long uid ,int controllerIndex, out uint? slotID)
|
||||||
|
{
|
||||||
|
slotID = null;
|
||||||
|
|
||||||
|
//controllerIndex取值返回[0,3],这个序号代表玩家本地的手柄编号
|
||||||
|
//todo : 根据uid和controllerIndex 返回占用的位置
|
||||||
|
|
||||||
|
//目前未实现,所有非0号位置的手柄,都返回false
|
||||||
|
if (controllerIndex != 0) return false;
|
||||||
|
|
||||||
|
if (roomMiniInfo.Player1UID == uid) slotID = 0;
|
||||||
|
if (roomMiniInfo.Player2UID == uid) slotID = 1;
|
||||||
|
if (roomMiniInfo.Player3UID == uid) slotID = 2;
|
||||||
|
if (roomMiniInfo.Player4UID == uid) slotID = 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 按照房间玩家下标获取昵称
|
/// 按照房间玩家下标获取昵称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -129,6 +129,11 @@ namespace AxibugEmuOnline.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IControllerSetuper GetControllerSetuper()
|
||||||
|
{
|
||||||
|
return ControllerMapper;
|
||||||
|
}
|
||||||
|
|
||||||
public ControllerState FromNet(AxiReplay.ReplayStep step)
|
public ControllerState FromNet(AxiReplay.ReplayStep step)
|
||||||
{
|
{
|
||||||
var temp = new ServerInputSnapShot();
|
var temp = new ServerInputSnapShot();
|
||||||
|
@ -3,77 +3,69 @@ using VirtualNes.Core;
|
|||||||
|
|
||||||
namespace AxibugEmuOnline.Client
|
namespace AxibugEmuOnline.Client
|
||||||
{
|
{
|
||||||
public class NesControllerMapper
|
public class NesControllerMapper : IControllerSetuper
|
||||||
{
|
{
|
||||||
public MapperSetter Player1 = new MapperSetter(1);
|
public Controller Controller0 { get; } = new(0);
|
||||||
public MapperSetter Player2 = new MapperSetter(2);
|
public Controller Controller1 { get; } = new(1);
|
||||||
public MapperSetter Player3 = new MapperSetter(3);
|
public Controller Controller2 { get; } = new(2);
|
||||||
public MapperSetter Player4 = new MapperSetter(4);
|
public Controller Controller3 { get; } = new(3);
|
||||||
|
|
||||||
|
private readonly EnumButtonType[] m_states = new EnumButtonType[4];
|
||||||
|
|
||||||
public ControllerState CreateState()
|
public ControllerState CreateState()
|
||||||
{
|
{
|
||||||
var state1 = Player1.GetButtons();
|
m_states[0] = m_states[1] = m_states[2] = m_states[3] = 0;
|
||||||
var state2 = Player2.GetButtons();
|
|
||||||
var state3 = Player3.GetButtons();
|
|
||||||
var state4 = Player4.GetButtons();
|
|
||||||
|
|
||||||
var result = new ControllerState(state1, state2, state3, state4);
|
if (Controller0.ConnectSlot.HasValue) m_states[Controller0.ConnectSlot.Value] = Controller0.GetButtons();
|
||||||
|
if (Controller1.ConnectSlot.HasValue) m_states[Controller1.ConnectSlot.Value] = Controller1.GetButtons();
|
||||||
|
if (Controller2.ConnectSlot.HasValue) m_states[Controller2.ConnectSlot.Value] = Controller2.GetButtons();
|
||||||
|
if (Controller3.ConnectSlot.HasValue) m_states[Controller3.ConnectSlot.Value] = Controller3.GetButtons();
|
||||||
|
|
||||||
|
var result = new ControllerState(m_states);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Mapper
|
/// <summary>
|
||||||
|
/// Nes控制器
|
||||||
|
/// </summary>
|
||||||
|
public class Controller
|
||||||
{
|
{
|
||||||
MapperSetter m_setter;
|
/// <summary>
|
||||||
EnumButtonType m_buttonType;
|
/// 控制器编号
|
||||||
IKeyListener m_keyListener;
|
/// <para><c>此编号并非对应游戏中的player1,player2,player3,player4,仅仅作为本地4个手柄的实例</c></para>
|
||||||
int m_controllerIndex => m_setter.ControllerIndex;
|
/// <value>[0,3]</value>
|
||||||
|
/// </summary>
|
||||||
public Mapper(MapperSetter setter, EnumButtonType buttonType)
|
|
||||||
{
|
|
||||||
m_setter = setter;
|
|
||||||
m_buttonType = buttonType;
|
|
||||||
|
|
||||||
loadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadConfig()
|
|
||||||
{
|
|
||||||
m_keyListener = MapperSetter.GetKey(m_controllerIndex, m_buttonType);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnumButtonType SampleKey()
|
|
||||||
{
|
|
||||||
return m_keyListener.IsPressing() ? m_buttonType : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MapperSetter
|
|
||||||
{
|
|
||||||
/// <summary> 控制器序号(手柄1,2,3,4) </summary>
|
|
||||||
public int ControllerIndex { get; }
|
public int ControllerIndex { get; }
|
||||||
public Mapper UP { get; private set; }
|
|
||||||
public Mapper DOWN { get; private set; }
|
|
||||||
public Mapper LEFT { get; private set; }
|
|
||||||
public Mapper RIGHT { get; private set; }
|
|
||||||
public Mapper A { get; private set; }
|
|
||||||
public Mapper B { get; private set; }
|
|
||||||
public Mapper SELECT { get; private set; }
|
|
||||||
public Mapper START { get; private set; }
|
|
||||||
public Mapper MIC { get; private set; }
|
|
||||||
|
|
||||||
public MapperSetter(int controllerIndex)
|
/// <summary>
|
||||||
|
/// 指示该手柄连接的手柄插槽
|
||||||
|
/// <para><c>这个值代表了该手柄在实际游戏中控制的Player</c></para>
|
||||||
|
/// <value>[0,3] 例外:为空代表未连接</value>
|
||||||
|
/// </summary>
|
||||||
|
public uint? ConnectSlot { get; set; }
|
||||||
|
|
||||||
|
public Button UP { get; }
|
||||||
|
public Button DOWN { get; }
|
||||||
|
public Button LEFT { get; }
|
||||||
|
public Button RIGHT { get; }
|
||||||
|
public Button A { get; }
|
||||||
|
public Button B { get; }
|
||||||
|
public Button SELECT { get; }
|
||||||
|
public Button START { get; }
|
||||||
|
public Button MIC { get; }
|
||||||
|
|
||||||
|
public Controller(int controllerIndex)
|
||||||
{
|
{
|
||||||
ControllerIndex = controllerIndex;
|
ControllerIndex = controllerIndex;
|
||||||
UP = new Mapper(this, EnumButtonType.UP);
|
UP = new Button(this, EnumButtonType.UP);
|
||||||
DOWN = new Mapper(this, EnumButtonType.DOWN);
|
DOWN = new Button(this, EnumButtonType.DOWN);
|
||||||
LEFT = new Mapper(this, EnumButtonType.LEFT);
|
LEFT = new Button(this, EnumButtonType.LEFT);
|
||||||
RIGHT = new Mapper(this, EnumButtonType.RIGHT);
|
RIGHT = new Button(this, EnumButtonType.RIGHT);
|
||||||
A = new Mapper(this, EnumButtonType.A);
|
A = new Button(this, EnumButtonType.A);
|
||||||
B = new Mapper(this, EnumButtonType.B);
|
B = new Button(this, EnumButtonType.B);
|
||||||
SELECT = new Mapper(this, EnumButtonType.SELECT);
|
SELECT = new Button(this, EnumButtonType.SELECT);
|
||||||
START = new Mapper(this, EnumButtonType.START);
|
START = new Button(this, EnumButtonType.START);
|
||||||
MIC = new Mapper(this, EnumButtonType.MIC);
|
MIC = new Button(this, EnumButtonType.MIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnumButtonType GetButtons()
|
public EnumButtonType GetButtons()
|
||||||
@ -93,7 +85,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IKeyListener GetKey(int controllerInput, EnumButtonType nesConBtnType)
|
public static KeyListener GetKey(int controllerInput, EnumButtonType nesConBtnType)
|
||||||
{
|
{
|
||||||
string configKey = $"NES_{controllerInput}_{nesConBtnType}";
|
string configKey = $"NES_{controllerInput}_{nesConBtnType}";
|
||||||
if (PlayerPrefs.HasKey(configKey))
|
if (PlayerPrefs.HasKey(configKey))
|
||||||
@ -102,62 +94,60 @@ namespace AxibugEmuOnline.Client
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var defaultKeyCode = GetDefaultKey();
|
var defaultKeyCode = KeyListener.GetDefaultKey(controllerInput, nesConBtnType);
|
||||||
PlayerPrefs.SetString(configKey, defaultKeyCode.ToString());
|
PlayerPrefs.SetString(configKey, defaultKeyCode.ToString());
|
||||||
return defaultKeyCode;
|
return defaultKeyCode;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeyListener GetDefaultKey()
|
/// <summary>
|
||||||
|
/// NES控制器按键类
|
||||||
|
/// </summary>
|
||||||
|
public class Button
|
||||||
{
|
{
|
||||||
switch (controllerInput)
|
/// <summary> 所属控制器 </summary>
|
||||||
|
readonly Controller m_hostController;
|
||||||
|
|
||||||
|
/// <summary> 按键 </summary>
|
||||||
|
readonly EnumButtonType m_buttonType;
|
||||||
|
|
||||||
|
/// <summary> 按键监听器 </summary>
|
||||||
|
KeyListener m_keyListener;
|
||||||
|
|
||||||
|
public Button(Controller controller, EnumButtonType buttonType)
|
||||||
{
|
{
|
||||||
case 1:
|
m_hostController = controller;
|
||||||
if (nesConBtnType == EnumButtonType.LEFT) return new KeyListener(KeyCode.A);
|
m_buttonType = buttonType;
|
||||||
if (nesConBtnType == EnumButtonType.RIGHT) return new KeyListener(KeyCode.D);
|
|
||||||
if (nesConBtnType == EnumButtonType.UP) return new KeyListener(KeyCode.W);
|
CreateListener();
|
||||||
if (nesConBtnType == EnumButtonType.DOWN) return new KeyListener(KeyCode.S);
|
|
||||||
if (nesConBtnType == EnumButtonType.START) return new KeyListener(KeyCode.B);
|
|
||||||
if (nesConBtnType == EnumButtonType.SELECT) return new KeyListener(KeyCode.V);
|
|
||||||
if (nesConBtnType == EnumButtonType.A) return new KeyListener(KeyCode.K);
|
|
||||||
if (nesConBtnType == EnumButtonType.B) return new KeyListener(KeyCode.J);
|
|
||||||
if (nesConBtnType == EnumButtonType.MIC) return new KeyListener(KeyCode.M);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (nesConBtnType == EnumButtonType.LEFT) return new KeyListener(KeyCode.Delete);
|
|
||||||
if (nesConBtnType == EnumButtonType.RIGHT) return new KeyListener(KeyCode.PageDown);
|
|
||||||
if (nesConBtnType == EnumButtonType.UP) return new KeyListener(KeyCode.Home);
|
|
||||||
if (nesConBtnType == EnumButtonType.DOWN) return new KeyListener(KeyCode.End);
|
|
||||||
if (nesConBtnType == EnumButtonType.START) return new KeyListener(KeyCode.PageUp);
|
|
||||||
if (nesConBtnType == EnumButtonType.SELECT) return new KeyListener(KeyCode.Insert);
|
|
||||||
if (nesConBtnType == EnumButtonType.A) return new KeyListener(KeyCode.Keypad5);
|
|
||||||
if (nesConBtnType == EnumButtonType.B) return new KeyListener(KeyCode.Keypad4);
|
|
||||||
if (nesConBtnType == EnumButtonType.MIC) return new KeyListener(KeyCode.KeypadPeriod);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
/// <summary>
|
||||||
}
|
/// 采集按钮按下状态
|
||||||
|
/// </summary>
|
||||||
}
|
/// <returns></returns>
|
||||||
}
|
public EnumButtonType SampleKey()
|
||||||
|
|
||||||
public interface IKeyListener
|
|
||||||
{
|
{
|
||||||
bool IsPressing();
|
return m_keyListener.IsPressing() ? m_buttonType : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct KeyListener : IKeyListener
|
private void CreateListener()
|
||||||
{
|
{
|
||||||
private KeyCode m_key;
|
m_keyListener = Controller.GetKey(m_hostController.ControllerIndex, m_buttonType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct KeyListener
|
||||||
|
{
|
||||||
|
private readonly KeyCode m_key;
|
||||||
|
|
||||||
public KeyListener(KeyCode key)
|
public KeyListener(KeyCode key)
|
||||||
{
|
{
|
||||||
m_key = key;
|
m_key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary> 从配置字符串构建 </summary>
|
||||||
/// 从配置表字符串构建
|
|
||||||
/// </summary>
|
|
||||||
public KeyListener(string confStr)
|
public KeyListener(string confStr)
|
||||||
{
|
{
|
||||||
m_key = KeyCode.None;
|
m_key = KeyCode.None;
|
||||||
@ -166,19 +156,83 @@ namespace AxibugEmuOnline.Client
|
|||||||
m_key = (KeyCode)result;
|
m_key = (KeyCode)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly bool IsPressing()
|
public bool IsPressing()
|
||||||
{
|
{
|
||||||
if (Input.GetKey(m_key)) return true;
|
return Input.GetKey(m_key);
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return ((int)(m_key)).ToString();
|
return ((int)(m_key)).ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static KeyListener GetDefaultKey(int controllerIndex, EnumButtonType nesConBtnType)
|
||||||
|
{
|
||||||
|
switch (controllerIndex)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
switch (nesConBtnType)
|
||||||
|
{
|
||||||
|
case EnumButtonType.LEFT:
|
||||||
|
return new KeyListener(KeyCode.A);
|
||||||
|
case EnumButtonType.RIGHT:
|
||||||
|
return new KeyListener(KeyCode.D);
|
||||||
|
case EnumButtonType.UP:
|
||||||
|
return new KeyListener(KeyCode.W);
|
||||||
|
case EnumButtonType.DOWN:
|
||||||
|
return new KeyListener(KeyCode.S);
|
||||||
|
case EnumButtonType.START:
|
||||||
|
return new KeyListener(KeyCode.B);
|
||||||
|
case EnumButtonType.SELECT:
|
||||||
|
return new KeyListener(KeyCode.V);
|
||||||
|
case EnumButtonType.A:
|
||||||
|
return new KeyListener(KeyCode.K);
|
||||||
|
case EnumButtonType.B:
|
||||||
|
return new KeyListener(KeyCode.J);
|
||||||
|
case EnumButtonType.MIC:
|
||||||
|
return new KeyListener(KeyCode.M);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
switch (nesConBtnType)
|
||||||
|
{
|
||||||
|
case EnumButtonType.LEFT:
|
||||||
|
return new KeyListener(KeyCode.Delete);
|
||||||
|
case EnumButtonType.RIGHT:
|
||||||
|
return new KeyListener(KeyCode.PageDown);
|
||||||
|
case EnumButtonType.UP:
|
||||||
|
return new KeyListener(KeyCode.Home);
|
||||||
|
case EnumButtonType.DOWN:
|
||||||
|
return new KeyListener(KeyCode.End);
|
||||||
|
case EnumButtonType.START:
|
||||||
|
return new KeyListener(KeyCode.PageUp);
|
||||||
|
case EnumButtonType.SELECT:
|
||||||
|
return new KeyListener(KeyCode.Insert);
|
||||||
|
case EnumButtonType.A:
|
||||||
|
return new KeyListener(KeyCode.Keypad5);
|
||||||
|
case EnumButtonType.B:
|
||||||
|
return new KeyListener(KeyCode.Keypad4);
|
||||||
|
case EnumButtonType.MIC:
|
||||||
|
return new KeyListener(KeyCode.KeypadPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetConnect(uint? con0ToSlot = null,
|
||||||
|
uint? con1ToSlot = null,
|
||||||
|
uint? con2ToSlot = null,
|
||||||
|
uint? con3ToSlot = null)
|
||||||
|
{
|
||||||
|
Controller0.ConnectSlot = con0ToSlot;
|
||||||
|
Controller1.ConnectSlot = con1ToSlot;
|
||||||
|
Controller2.ConnectSlot = con2ToSlot;
|
||||||
|
Controller3.ConnectSlot = con3ToSlot;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,44 +1,61 @@
|
|||||||
using AxibugEmuOnline.Client.ClientCore;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using AxibugEmuOnline.Client.ClientCore;
|
||||||
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using VirtualNes.Core;
|
using VirtualNes.Core;
|
||||||
using VirtualNes.Core.Debug;
|
using VirtualNes.Core.Debug;
|
||||||
|
using Debug = System.Diagnostics.Debug;
|
||||||
|
|
||||||
namespace AxibugEmuOnline.Client
|
namespace AxibugEmuOnline.Client
|
||||||
{
|
{
|
||||||
public class NesEmulator : MonoBehaviour, IEmuCore
|
public class NesEmulator : MonoBehaviour, IEmuCore
|
||||||
{
|
{
|
||||||
public EnumPlatform Platform => EnumPlatform.NES;
|
public VideoProvider VideoProvider;
|
||||||
|
public AudioProvider AudioProvider;
|
||||||
|
|
||||||
//模拟器核心实例化对象
|
//模拟器核心实例化对象
|
||||||
public NES NesCore { get; private set; }
|
public NES NesCore { get; private set; }
|
||||||
|
|
||||||
//视频驱动(这里是Unity接收模拟器画面数据的并渲染出来的实现)
|
/// <summary> 是否暂停 </summary>
|
||||||
public VideoProvider VideoProvider;
|
public bool IsPause { get; private set; }
|
||||||
//音频驱动(这里是Unity接收模拟器音频数据的并播放出来的实现)
|
|
||||||
public AudioProvider AudioProvider;
|
|
||||||
//是否暂停
|
|
||||||
private bool m_bPause;
|
|
||||||
/// <summary> 是否暂停 </summary>
|
|
||||||
public bool IsPause => m_bPause;
|
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
//关闭垂直同步
|
//关闭垂直同步
|
||||||
QualitySettings.vSyncCount = 0;
|
QualitySettings.vSyncCount = 0;
|
||||||
//设为60帧
|
//设为60帧
|
||||||
Application.targetFrameRate = 60;
|
Application.targetFrameRate = 60;
|
||||||
VideoProvider.NesEmu = this;
|
VideoProvider.NesEmu = this;
|
||||||
AudioProvider.NesEmu = this;
|
AudioProvider.NesEmu = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 指定ROM开始游戏
|
/// Unity的逐帧驱动
|
||||||
|
/// </summary>
|
||||||
|
private unsafe void Update()
|
||||||
|
{
|
||||||
|
if (IsPause) return;
|
||||||
|
|
||||||
|
if (NesCore != null)
|
||||||
|
{
|
||||||
|
PushEmulatorFrame();
|
||||||
|
if (InGameUI.Instance.IsNetPlay)
|
||||||
|
FixEmulatorFrame();
|
||||||
|
|
||||||
|
var screenBuffer = NesCore.ppu.GetScreenPtr();
|
||||||
|
VideoProvider.SetDrawData(screenBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumPlatform Platform => EnumPlatform.NES;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指定ROM开始游戏
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rom"></param>
|
|
||||||
public void StartGame(RomFile rom)
|
public void StartGame(RomFile rom)
|
||||||
{
|
{
|
||||||
StopGame();
|
StopGame();
|
||||||
@ -59,78 +76,14 @@ namespace AxibugEmuOnline.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 停止游戏
|
|
||||||
/// </summary>
|
|
||||||
public void StopGame()
|
|
||||||
{
|
|
||||||
NesCore?.Dispose();
|
|
||||||
NesCore = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unity的逐帧驱动
|
|
||||||
/// </summary>
|
|
||||||
private unsafe void Update()
|
|
||||||
{
|
|
||||||
if (m_bPause) return;
|
|
||||||
|
|
||||||
if (NesCore != null)
|
|
||||||
{
|
|
||||||
PushEmulatorFrame();
|
|
||||||
if (InGameUI.Instance.IsNetPlay)
|
|
||||||
FixEmulatorFrame();
|
|
||||||
|
|
||||||
var screenBuffer = NesCore.ppu.GetScreenPtr();
|
|
||||||
VideoProvider.SetDrawData(screenBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//是否跳帧,单机无效
|
|
||||||
private void FixEmulatorFrame()
|
|
||||||
{
|
|
||||||
var skipFrameCount = App.roomMgr.netReplay.GetSkipFrameCount();
|
|
||||||
|
|
||||||
if (skipFrameCount > 0) App.log.Debug($"SKIP FRAME : {skipFrameCount}");
|
|
||||||
for (int i = 0; i < skipFrameCount; i++)
|
|
||||||
{
|
|
||||||
if (!PushEmulatorFrame()) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ControllerState lastState;
|
|
||||||
//推进帧
|
|
||||||
private bool PushEmulatorFrame()
|
|
||||||
{
|
|
||||||
Supporter.SampleInput(NesCore.FrameCount);
|
|
||||||
var controlState = Supporter.GetControllerState();
|
|
||||||
|
|
||||||
//如果未收到Input数据,核心帧不推进
|
|
||||||
if (!controlState.valid) return false;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
if (controlState != lastState)
|
|
||||||
{
|
|
||||||
App.log.Info($"[LOCALDEBUG]{NesCore.FrameCount}-->{controlState}");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NesCore.pad.Sync(controlState);
|
|
||||||
NesCore.EmulateFrame(true);
|
|
||||||
|
|
||||||
lastState = controlState;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pause()
|
public void Pause()
|
||||||
{
|
{
|
||||||
m_bPause = true;
|
IsPause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume()
|
public void Resume()
|
||||||
{
|
{
|
||||||
m_bPause = false;
|
IsPause = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -155,7 +108,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取即时存档
|
/// 获取即时存档
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public byte[] GetStateBytes()
|
public byte[] GetStateBytes()
|
||||||
@ -164,21 +117,68 @@ namespace AxibugEmuOnline.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 加载即时存档
|
/// 加载即时存档
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data"></param>
|
/// <param
|
||||||
|
/// name="data">
|
||||||
|
/// </param>
|
||||||
public void LoadStateFromBytes(byte[] data)
|
public void LoadStateFromBytes(byte[] data)
|
||||||
{
|
{
|
||||||
State st = new State();
|
var st = new State();
|
||||||
st.FromByte(data);
|
st.FromByte(data);
|
||||||
NesCore.LoadState(st);
|
NesCore.LoadState(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Frame => NesCore.FrameCount;
|
public uint Frame => NesCore.FrameCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止游戏
|
||||||
|
/// </summary>
|
||||||
|
public void StopGame()
|
||||||
|
{
|
||||||
|
NesCore?.Dispose();
|
||||||
|
NesCore = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private ControllerState m_lastState;
|
||||||
|
#endif
|
||||||
|
//是否跳帧,单机无效
|
||||||
|
private void FixEmulatorFrame()
|
||||||
|
{
|
||||||
|
var skipFrameCount = App.roomMgr.netReplay.GetSkipFrameCount();
|
||||||
|
|
||||||
|
if (skipFrameCount > 0) App.log.Debug($"SKIP FRAME : {skipFrameCount}");
|
||||||
|
for (var i = 0; i < skipFrameCount; i++)
|
||||||
|
if (!PushEmulatorFrame())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//推进帧
|
||||||
|
private bool PushEmulatorFrame()
|
||||||
|
{
|
||||||
|
Supporter.SampleInput(NesCore.FrameCount);
|
||||||
|
var controlState = Supporter.GetControllerState();
|
||||||
|
|
||||||
|
//如果未收到Input数据,核心帧不推进
|
||||||
|
if (!controlState.valid) return false;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (controlState != m_lastState) App.log.Info($"[LOCALDEBUG]{NesCore.FrameCount}-->{controlState}");
|
||||||
|
m_lastState = controlState;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NesCore.pad.Sync(controlState);
|
||||||
|
NesCore.EmulateFrame(true);
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 编辑器用
|
/// 编辑器用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Conditional("UNITY_EDITOR")]
|
[Conditional("UNITY_EDITOR")]
|
||||||
[ContextMenu("ImportNesDB")]
|
[ContextMenu("ImportNesDB")]
|
||||||
@ -189,20 +189,21 @@ namespace AxibugEmuOnline.Client
|
|||||||
|
|
||||||
var xmlStr = File.ReadAllText("nes20db.xml");
|
var xmlStr = File.ReadAllText("nes20db.xml");
|
||||||
var xml = XDocument.Parse(xmlStr);
|
var xml = XDocument.Parse(xmlStr);
|
||||||
var games = xml.Element("nes20db").Elements("game");
|
var games = xml.Element("nes20db")?.Elements("game");
|
||||||
|
Debug.Assert(games != null, nameof(games) + " != null");
|
||||||
foreach (var game in games)
|
foreach (var game in games)
|
||||||
{
|
{
|
||||||
var crcStr = game.Element("rom").Attribute("crc32").Value;
|
var crcStr = game.Element("rom")?.Attribute("crc32")?.Value;
|
||||||
var crc = uint.Parse($"{crcStr}", System.Globalization.NumberStyles.HexNumber);
|
var crc = uint.Parse($"{crcStr}", NumberStyles.HexNumber);
|
||||||
|
|
||||||
var mapper = int.Parse($"{game.Element("pcb").Attribute("mapper").Value}");
|
var mapper = int.Parse($"{game.Element("pcb")?.Attribute("mapper")?.Value}");
|
||||||
|
|
||||||
if (mapper > 255) continue;
|
if (mapper > 255) continue;
|
||||||
db.AddInfo(new RomDB.RomInfo { CRC = crc, Mapper = mapper });
|
db.AddInfo(new RomDB.RomInfo { CRC = crc, Mapper = mapper });
|
||||||
}
|
}
|
||||||
|
|
||||||
UnityEditor.EditorUtility.SetDirty(db);
|
EditorUtility.SetDirty(db);
|
||||||
UnityEditor.AssetDatabase.SaveAssets();
|
AssetDatabase.SaveAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using AxibugEmuOnline.Client.ClientCore;
|
using AxibugEmuOnline.Client.ClientCore;
|
||||||
using AxibugEmuOnline.Client.UI;
|
using AxibugEmuOnline.Client.UI;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
@ -59,7 +59,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
if (!m_romfile.InfoReady)
|
if (!m_romfile.InfoReady)
|
||||||
{
|
{
|
||||||
SetBaseInfo("正在拉取", "---", "---");
|
SetBaseInfo("正在拉取", "---", "---");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -82,8 +82,6 @@ namespace AxibugEmuOnline.Client
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//加载一个用户自己提供的Rom时,使用这个方法
|
|
||||||
//App.emu.BeginGame(App.nesRomLib.GetExistRom("bad_apple_2_5.nes"));
|
|
||||||
App.emu.BeginGame(m_romfile);
|
App.emu.BeginGame(m_romfile);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
using AxibugEmuOnline.Client.ClientCore;
|
using System.Collections.Generic;
|
||||||
|
using AxibugEmuOnline.Client.ClientCore;
|
||||||
using AxibugEmuOnline.Client.Event;
|
using AxibugEmuOnline.Client.Event;
|
||||||
using System;
|
using AxibugProtobuf;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace AxibugEmuOnline.Client
|
namespace AxibugEmuOnline.Client
|
||||||
{
|
{
|
||||||
public class InGameUI : CommandExecuter
|
public class InGameUI : CommandExecuter
|
||||||
{
|
{
|
||||||
|
private bool m_delayCreateRoom;
|
||||||
|
private object m_state;
|
||||||
|
private StepPerformer m_stepPerformer;
|
||||||
|
|
||||||
|
private readonly List<OptionMenu> menus = new();
|
||||||
public static InGameUI Instance { get; private set; }
|
public static InGameUI Instance { get; private set; }
|
||||||
|
|
||||||
public RomFile RomFile => m_rom;
|
public RomFile RomFile { get; private set; }
|
||||||
|
|
||||||
public override bool Enable => gameObject.activeInHierarchy;
|
public override bool Enable => gameObject.activeInHierarchy;
|
||||||
|
|
||||||
/// <summary> 指示该游戏实例是否处于联机模式 </summary>
|
/// <summary> 指示该游戏实例是否处于联机模式 </summary>
|
||||||
@ -19,18 +25,13 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
if (!App.user.IsLoggedIn) return false;
|
if (!App.user.IsLoggedIn) return false;
|
||||||
if (App.roomMgr.mineRoomMiniInfo == null) return false;
|
if (App.roomMgr.mineRoomMiniInfo == null) return false;
|
||||||
if (App.roomMgr.RoomState <= AxibugProtobuf.RoomGameState.OnlyHost) return false;
|
if (App.roomMgr.RoomState <= RoomGameState.OnlyHost) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RomFile m_rom;
|
|
||||||
public IEmuCore Core { get; private set; }
|
public IEmuCore Core { get; private set; }
|
||||||
private object m_state;
|
|
||||||
|
|
||||||
private List<OptionMenu> menus = new List<OptionMenu>();
|
|
||||||
private StepPerformer m_stepPerformer;
|
|
||||||
|
|
||||||
protected override void Awake()
|
protected override void Awake()
|
||||||
{
|
{
|
||||||
@ -59,31 +60,31 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
m_state = state;
|
m_state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 读取快速快照
|
/// 读取快速快照
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
public object GetQuickState()
|
public object GetQuickState()
|
||||||
{
|
{
|
||||||
return m_state;
|
return m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool m_delayCreateRoom;
|
|
||||||
public void Show(RomFile currentRom, IEmuCore core)
|
public void Show(RomFile currentRom, IEmuCore core)
|
||||||
{
|
{
|
||||||
m_delayCreateRoom = false;
|
m_delayCreateRoom = false;
|
||||||
m_state = null;//清空游戏快照
|
m_state = null; //清空游戏快照
|
||||||
CommandDispatcher.Instance.RegistController(this);
|
CommandDispatcher.Instance.RegistController(this);
|
||||||
|
|
||||||
m_rom = currentRom;
|
RomFile = currentRom;
|
||||||
Core = core;
|
Core = core;
|
||||||
m_stepPerformer.Reset();
|
m_stepPerformer.Reset();
|
||||||
|
|
||||||
if (!App.roomMgr.InRoom)
|
if (!App.roomMgr.InRoom)
|
||||||
{
|
{
|
||||||
if (App.user.IsLoggedIn)
|
if (App.user.IsLoggedIn)
|
||||||
App.roomMgr.SendCreateRoom(m_rom.ID, 0, m_rom.Hash);
|
{
|
||||||
|
App.roomMgr.SendCreateRoom(RomFile.ID, 0, RomFile.Hash);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_delayCreateRoom = true;
|
m_delayCreateRoom = true;
|
||||||
@ -115,10 +116,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
|
|
||||||
private void OnLoggedIn()
|
private void OnLoggedIn()
|
||||||
{
|
{
|
||||||
if (m_delayCreateRoom)
|
if (m_delayCreateRoom) App.roomMgr.SendCreateRoom(RomFile.ID, 0, RomFile.Hash);
|
||||||
{
|
|
||||||
App.roomMgr.SendCreateRoom(m_rom.ID, 0, m_rom.Hash);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnServerStepUpdate(int step)
|
private void OnServerStepUpdate(int step)
|
||||||
@ -138,16 +136,17 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
OverlayManager.PopSideBar(menus, 0, PopMenu_OnHide);
|
OverlayManager.PopSideBar(menus, 0, PopMenu_OnHide);
|
||||||
|
|
||||||
if (!IsNetPlay)//单人模式暂停模拟器
|
if (!IsNetPlay) //单人模式暂停模拟器
|
||||||
Core.Pause();
|
Core.Pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
//菜单关闭时候
|
//菜单关闭时候
|
||||||
private void PopMenu_OnHide()
|
private void PopMenu_OnHide()
|
||||||
{
|
{
|
||||||
if (!IsNetPlay)//单人模式恢复模拟器的暂停
|
if (!IsNetPlay) //单人模式恢复模拟器的暂停
|
||||||
Core.Resume();
|
Core.Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QuitGame()
|
public void QuitGame()
|
||||||
{
|
{
|
||||||
Eventer.Instance.UnregisterEvent<int>(EEvent.OnRoomWaitStepChange, OnServerStepUpdate);
|
Eventer.Instance.UnregisterEvent<int>(EEvent.OnRoomWaitStepChange, OnServerStepUpdate);
|
||||||
|
@ -6,7 +6,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
public class StepPerformer
|
public class StepPerformer
|
||||||
{
|
{
|
||||||
private InGameUI m_inGameUI;
|
private readonly InGameUI m_inGameUI;
|
||||||
private int m_step = -1;
|
private int m_step = -1;
|
||||||
|
|
||||||
public StepPerformer(InGameUI inGameUI)
|
public StepPerformer(InGameUI inGameUI)
|
||||||
|
@ -1,28 +1,25 @@
|
|||||||
using AxibugEmuOnline.Client.ClientCore;
|
using AxibugEmuOnline.Client.ClientCore;
|
||||||
using AxibugEmuOnline.Client.Event;
|
using AxibugEmuOnline.Client.Event;
|
||||||
using AxibugEmuOnline.Client.Manager;
|
using AxibugEmuOnline.Client.Manager;
|
||||||
using AxibugEmuOnline.Client.UI;
|
using AxibugEmuOnline.Client.UI;
|
||||||
using AxibugProtobuf;
|
using AxibugProtobuf;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
using Debug = System.Diagnostics.Debug;
|
||||||
|
|
||||||
namespace AxibugEmuOnline.Client
|
namespace AxibugEmuOnline.Client
|
||||||
{
|
{
|
||||||
public class RoomItem : MenuItem, IVirtualItem
|
public class RoomItem : MenuItem, IVirtualItem
|
||||||
{
|
{
|
||||||
[SerializeField]
|
[SerializeField] Image m_roomPreview;
|
||||||
Image m_roomPreview;
|
[SerializeField] Slider m_downloadProgress;
|
||||||
[SerializeField]
|
[SerializeField] GameObject m_downloadingFlag;
|
||||||
Slider m_downloadProgress;
|
[SerializeField] GameObject m_romReadyFlag;
|
||||||
[SerializeField]
|
|
||||||
GameObject m_downloadingFlag;
|
|
||||||
[SerializeField]
|
|
||||||
GameObject m_romReadyFlag;
|
|
||||||
|
|
||||||
private RomFile m_romFile;
|
private RomFile m_romFile;
|
||||||
|
|
||||||
public int Index { get; set; }
|
public int Index { get; set; }
|
||||||
public int roomID { get; private set; }
|
public int RoomID { get; private set; }
|
||||||
|
|
||||||
protected override void Awake()
|
protected override void Awake()
|
||||||
{
|
{
|
||||||
@ -33,7 +30,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
|
|
||||||
private void OnRoomSignelUpdate(int roomID)
|
private void OnRoomSignelUpdate(int roomID)
|
||||||
{
|
{
|
||||||
if (this.roomID != roomID) return;
|
if (this.RoomID != roomID) return;
|
||||||
|
|
||||||
if (App.roomMgr.GetRoomListMiniInfo(roomID, out var roomInfo))
|
if (App.roomMgr.GetRoomListMiniInfo(roomID, out var roomInfo))
|
||||||
UpdateUI(roomInfo);
|
UpdateUI(roomInfo);
|
||||||
@ -41,8 +38,10 @@ namespace AxibugEmuOnline.Client
|
|||||||
|
|
||||||
public void SetData(object data)
|
public void SetData(object data)
|
||||||
{
|
{
|
||||||
var roomInfo = data as Protobuf_Room_MiniInfo;
|
Debug.Assert(data is Protobuf_Room_MiniInfo);
|
||||||
roomID = roomInfo.RoomID;
|
|
||||||
|
var roomInfo = (Protobuf_Room_MiniInfo)data;
|
||||||
|
RoomID = roomInfo.RoomID;
|
||||||
|
|
||||||
UpdateUI(roomInfo);
|
UpdateUI(roomInfo);
|
||||||
}
|
}
|
||||||
@ -58,20 +57,19 @@ namespace AxibugEmuOnline.Client
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!App.roomMgr.GetRoomListMiniInfo(roomID, out Protobuf_Room_MiniInfo MiniInfo))
|
if (!App.roomMgr.GetRoomListMiniInfo(RoomID, out Protobuf_Room_MiniInfo MiniInfo))
|
||||||
{
|
{
|
||||||
OverlayManager.PopTip("房间不存在");
|
OverlayManager.PopTip("房间不存在");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] freeSlots = null;
|
if (!MiniInfo.GetFreeSlot(out var freeSlots))
|
||||||
if (!MiniInfo.GetFreeSlot(out freeSlots))
|
|
||||||
{
|
{
|
||||||
OverlayManager.PopTip("无空闲位置");
|
OverlayManager.PopTip("无空闲位置");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
App.roomMgr.SendJoinRoom(roomID, freeSlots[0]);
|
App.roomMgr.SendJoinRoom(RoomID, freeSlots[0]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,12 +78,12 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
var hostNick = roomInfo.GetHostNickName();
|
var hostNick = roomInfo.GetHostNickName();
|
||||||
roomInfo.GetRoomPlayers(out var cur, out var max);
|
roomInfo.GetRoomPlayers(out var cur, out var max);
|
||||||
SetBaseInfo("--", $"<b>{hostNick}</b>的房间", $"{cur}/{max}");
|
SetBaseInfo("--", $"<b>{hostNick}</b>的房间", $"{cur}/{max}");
|
||||||
SetIcon(null);
|
SetIcon(null);
|
||||||
|
|
||||||
roomInfo.FetchRomFileInRoomInfo(EnumPlatform.NES, (room, romFile) =>
|
roomInfo.FetchRomFileInRoomInfo(EnumPlatform.NES, (room, romFile) =>
|
||||||
{
|
{
|
||||||
if (room.RoomID != roomID) return;
|
if (room.RoomID != RoomID) return;
|
||||||
|
|
||||||
m_romFile = romFile;
|
m_romFile = romFile;
|
||||||
Txt.text = romFile.Alias;
|
Txt.text = romFile.Alias;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public struct ControllerState
|
public struct ControllerState : IEquatable<ControllerState>
|
||||||
{
|
{
|
||||||
public uint raw0;
|
public uint raw0;
|
||||||
public uint raw1;
|
public uint raw1;
|
||||||
@ -11,19 +11,49 @@ namespace VirtualNes.Core
|
|||||||
|
|
||||||
public bool valid;
|
public bool valid;
|
||||||
|
|
||||||
public ControllerState(
|
public ControllerState(EnumButtonType[] states)
|
||||||
EnumButtonType player0_buttons,
|
|
||||||
EnumButtonType player1_buttons,
|
|
||||||
EnumButtonType player2_buttons,
|
|
||||||
EnumButtonType player3_buttons)
|
|
||||||
{
|
{
|
||||||
raw0 = (uint)player0_buttons;
|
raw0 = (uint)states[0];
|
||||||
raw1 = (uint)player1_buttons;
|
raw1 = (uint)states[1];
|
||||||
raw2 = (uint)player2_buttons;
|
raw2 = (uint)states[2];
|
||||||
raw3 = (uint)player3_buttons;
|
raw3 = (uint)states[3];
|
||||||
valid = true;
|
valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasButton(int player, EnumButtonType button)
|
||||||
|
{
|
||||||
|
uint raw = player switch
|
||||||
|
{
|
||||||
|
0 => raw0,
|
||||||
|
1 => raw1,
|
||||||
|
2 => raw2,
|
||||||
|
3 => raw3,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
return (raw & (uint)button) == (uint)button;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{raw0}|{raw1}|{raw2}|{raw3}";
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Impl_Equals
|
||||||
|
public bool Equals(ControllerState other)
|
||||||
|
{
|
||||||
|
return raw0 == other.raw0 && raw1 == other.raw1 && raw2 == other.raw2 && raw3 == other.raw3 && valid == other.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is ControllerState other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(raw0, raw1, raw2, raw3, valid);
|
||||||
|
}
|
||||||
|
|
||||||
public static bool operator ==(ControllerState left, ControllerState right)
|
public static bool operator ==(ControllerState left, ControllerState right)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@ -37,24 +67,7 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
return !(left == right);
|
return !(left == right);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{raw0}|{raw1}|{raw2}|{raw3}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasButton(int player, EnumButtonType button)
|
|
||||||
{
|
|
||||||
uint raw = 0;
|
|
||||||
switch (player)
|
|
||||||
{
|
|
||||||
case 0: raw = raw0; break;
|
|
||||||
case 1: raw = raw1; break;
|
|
||||||
case 2: raw = raw2; break;
|
|
||||||
case 3: raw = raw3; break;
|
|
||||||
}
|
|
||||||
return (raw & (uint)button) == (uint)button;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
|
@ -5,6 +5,7 @@ namespace VirtualNes.Core
|
|||||||
public static class Supporter
|
public static class Supporter
|
||||||
{
|
{
|
||||||
private static ISupporterImpl s_support;
|
private static ISupporterImpl s_support;
|
||||||
|
|
||||||
public static void Setup(ISupporterImpl supporter)
|
public static void Setup(ISupporterImpl supporter)
|
||||||
{
|
{
|
||||||
s_support = supporter;
|
s_support = supporter;
|
||||||
@ -44,6 +45,7 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
s_support.SaveFile(fileData, directPath, fileName);
|
s_support.SaveFile(fileData, directPath, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream OpenFile(string directPath, string fileName)
|
public static Stream OpenFile(string directPath, string fileName)
|
||||||
{
|
{
|
||||||
return s_support.OpenFile(directPath, fileName);
|
return s_support.OpenFile(directPath, fileName);
|
||||||
@ -64,6 +66,11 @@ namespace VirtualNes.Core
|
|||||||
s_support.SampleInput(frameCount);
|
s_support.SampleInput(frameCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IControllerSetuper GetControllerSetuper()
|
||||||
|
{
|
||||||
|
return s_support.GetControllerSetuper();
|
||||||
|
}
|
||||||
|
|
||||||
public static EmulatorConfig Config => s_support.Config;
|
public static EmulatorConfig Config => s_support.Config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,5 +89,21 @@ namespace VirtualNes.Core
|
|||||||
bool TryGetMapperNo(ROM rom, out int mapperNo);
|
bool TryGetMapperNo(ROM rom, out int mapperNo);
|
||||||
ControllerState GetControllerState();
|
ControllerState GetControllerState();
|
||||||
void SampleInput(uint frameCount);
|
void SampleInput(uint frameCount);
|
||||||
|
IControllerSetuper GetControllerSetuper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 负责管理本地控制器与具体游戏之间的槽位分配
|
||||||
|
/// </summary>
|
||||||
|
public interface IControllerSetuper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设置本地手柄与游戏手柄槽位的映射
|
||||||
|
/// </summary>
|
||||||
|
void SetConnect(
|
||||||
|
uint? con0ToSlot = null,
|
||||||
|
uint? con1ToSlot = null,
|
||||||
|
uint? con2ToSlot = null,
|
||||||
|
uint? con3ToSlot = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user