diff --git a/NetInput.Capture/NetInput.Capture.cpp b/NetInput.Capture/NetInput.Capture.cpp index 50bd4f7..94345a9 100644 --- a/NetInput.Capture/NetInput.Capture.cpp +++ b/NetInput.Capture/NetInput.Capture.cpp @@ -12,14 +12,130 @@ #include #include #include +#include +#pragma comment(lib, "Netapi32.lib") + +typedef struct _ASTAT_ +{ + ADAPTER_STATUS adapt; + NAME_BUFFER NameBuff[30]; +} ASTAT, * PASTAT; + + +#define PAD_SERVER_SIZE 5 +#define PAD_CLIENT_SIZE 24 +#define MAC_SIZE 6 + +#define PAD_ONLINE 0x00u +#define PAD_VIBRA 0x01u +#define PAD_REG 0x03u +#define PAD_STATE 0x04u +#define PAD_RESET 0xFFu SOCKET sock; struct sockaddr_in addr; XINPUT_STATE lastSentInputStates[XUSER_MAX_COUNT]; +time_t updatetime[XUSER_MAX_COUNT]; +uint8_t hmac[MAC_SIZE]; + +bool checkvalidmac(uint8_t hMac[]) +{ + for (int i = 0; i < MAC_SIZE; i++) { + if (hMac[i] > 0x00u && hMac[i] < uint8_t(0xFFu)) + return true; + } + return false; +} + + +bool getMac() +{ + ASTAT Adapter; + NCB Ncb; + UCHAR uRetCode; + LANA_ENUM lenum; + int i = 0; + + memset(&Ncb, 0, sizeof(Ncb)); + Ncb.ncb_command = NCBENUM; + Ncb.ncb_buffer = (UCHAR*)&lenum; + Ncb.ncb_length = sizeof(lenum); + + uRetCode = Netbios(&Ncb); + + for (i = 0; i < lenum.length; i++) + { + memset(&Ncb, 0, sizeof(Ncb)); + Ncb.ncb_command = NCBRESET; + Ncb.ncb_lana_num = lenum.lana[i]; + uRetCode = Netbios(&Ncb); + + memset(&Ncb, 0, sizeof(Ncb)); + Ncb.ncb_command = NCBASTAT; + Ncb.ncb_lana_num = lenum.lana[i]; + strcpy_s((char*)Ncb.ncb_callname, sizeof(Ncb.ncb_callname), "*"); + Ncb.ncb_buffer = (unsigned char*)&Adapter; + Ncb.ncb_length = sizeof(Adapter); + uRetCode = Netbios(&Ncb); + + if (uRetCode == 0) + { + for (int j = 0; j < MAC_SIZE; j++) { + hmac[j] = int(Adapter.adapt.adapter_address[j]); + } + + if (checkvalidmac(hmac)) + return true; + } + } + return false; +} + + +void CheckControllerMessage() +{ + while (true) + { + char packet[PAD_SERVER_SIZE]; + int addr_size = sizeof(addr); + int bytesReceived = recvfrom(sock, (char*)&packet, sizeof(packet), 0, (struct sockaddr*)&addr, &addr_size); + + if (bytesReceived == PAD_SERVER_SIZE){ + uint8_t i = (uint8_t)packet[1]; + if ((i >= 0) && (i < XUSER_MAX_COUNT)) { + if (packet[0] == (uint8_t)PAD_ONLINE) { + XINPUT_VIBRATION Vibration; + + uint8_t pindex = (uint8_t)packet[4]; + Vibration.wLeftMotorSpeed = 0; + Vibration.wRightMotorSpeed = -1; + XInputSetState(i, &Vibration); + + printf("Connected client controller %u to server controller %u.\n", i,pindex); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + + Vibration.wLeftMotorSpeed = 0; + Vibration.wRightMotorSpeed = 0; + XInputSetState(i, &Vibration); + + } + else if (packet[0] == (uint8_t)PAD_VIBRA) { + XINPUT_VIBRATION Vibration; + Vibration.wLeftMotorSpeed = packet[2] << 8; + Vibration.wRightMotorSpeed = packet[3] << 8; + XInputSetState(i, &Vibration); + } + } + } + } +} + void SendResetControllers() { - uint8_t packet[] = { 0xFFu }; + uint8_t packet[1+ MAC_SIZE]; + packet[0] = (uint8_t)PAD_RESET; + memcpy(packet + 1, &hmac, MAC_SIZE); if (sendto(sock, (const char*)&packet, sizeof(packet), 0, (struct sockaddr*)&addr, sizeof(addr)) != sizeof(packet)) printf("Failed to send reset message.\n"); } @@ -27,42 +143,53 @@ void SendResetControllers() void PollControllers() { XINPUT_STATE state; - for (uint32_t i = 0u; i < XUSER_MAX_COUNT; i++) + for (uint8_t i = 0u; i < XUSER_MAX_COUNT; i++) { memset(&state, 0, sizeof(XINPUT_STATE)); if (XInputGetState(i, &state) != ERROR_SUCCESS) - continue; + continue; + + if (time(NULL) - updatetime[i] >= 5) + { + uint8_t packet[MAC_SIZE+2]; + packet[0] = (uint8_t)PAD_REG; + packet[1] = (uint8_t)i; + memcpy(packet + 2, &hmac, MAC_SIZE); + if (sendto(sock, (const char*)&packet, sizeof(packet), 0, (struct sockaddr*)&addr, sizeof(addr)) != sizeof(packet)) + printf("Failed to checktimeOut of client controller %u .\n",i); + updatetime[i] = time(NULL); + } if (memcmp(lastSentInputStates + i, &state, sizeof(XINPUT_STATE)) == 0) - continue; + continue; - uint8_t packet[sizeof(XINPUT_STATE) + 1]; - packet[0] = (uint8_t)i; - memcpy(packet + 1, &state, sizeof(XINPUT_STATE)); + uint8_t packet[PAD_CLIENT_SIZE]; + + packet[0] = (uint8_t)PAD_STATE; + packet[1] = (uint8_t)i; + + memcpy(packet + 2, &hmac, MAC_SIZE); + memcpy(packet + MAC_SIZE + 2, &state, sizeof(XINPUT_STATE)); memcpy(lastSentInputStates + i, &state, sizeof(XINPUT_STATE)); if (sendto(sock, (const char*)&packet, sizeof(packet), 0, (struct sockaddr*)&addr, sizeof(addr)) != sizeof(packet)) - printf("Failed to send update message of controller %u.\n", i); + printf("Failed to send update message of client controller %u.\n", i); } } -int main() +int main(int argc, char* argv[]) { std::string ip; - - std::ifstream input_file("target.txt"); - if (input_file.is_open()) - { - printf("Reading ip from target.txt.\n"); - ip = std::string((std::istreambuf_iterator(input_file)), std::istreambuf_iterator()); - - if (inet_pton(AF_INET, ip.c_str(), &(addr.sin_addr)) != 1) - { - std::cout << ip << " is not a valid ip, please correct target.txt\n"; - return -1; - } + int port = 4313; + + if (argc > 1) { + ip = argv[1]; } + if (argc > 2) { + sscanf_s(argv[2], "%d", &port); + } + if (ip.empty()) { while (true) @@ -77,12 +204,24 @@ int main() } } - printf("Target is %s.\n", ip.c_str()); + if (inet_pton(AF_INET, ip.c_str(), &(addr.sin_addr)) != 1) + { + std::cout << ip << " is not a valid ip, please try again ! \n"; + return -1; + } - CoInitialize(NULL); + if (!getMac()) { + printf("Failed to get mac address .\n"); + return -1; + } - printf("Starting networking...\n"); + printf("Connected to server %s:%d.\n", ip.c_str(),port); + CoInitialize(NULL); + + printf("Starting networking at %02X:%02X:%02X:%02X:%02X:%02X ...\n", + hmac[0], hmac[1], hmac[2], hmac[3], hmac[4], hmac[5]); + WSADATA wsaData; int wsaStartupResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (wsaStartupResult != 0) @@ -99,23 +238,20 @@ int main() } addr.sin_family = AF_INET; - addr.sin_port = htons(4313); + addr.sin_port = htons(port); - printf("Sending reset...\n"); - SendResetControllers(); - printf("Done.\n"); + printf("Waiting for gamepad input, press Ctrl+C to exit...\n"); + + std::thread message(CheckControllerMessage); // new thread for message - printf("Waiting for gamepad input, press ESC to exit...\n"); memset(lastSentInputStates, 0, sizeof(lastSentInputStates)); while (true) { - if (GetKeyState(VK_ESCAPE) & 0x8000) - break; - PollControllers(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } + printf("Wait For Closing...\n"); SendResetControllers(); closesocket(sock); WSACleanup(); diff --git a/NetInput.Player/NetInput.Player.cpp b/NetInput.Player/NetInput.Player.cpp index 923a3a9..bc7fe40 100644 --- a/NetInput.Player/NetInput.Player.cpp +++ b/NetInput.Player/NetInput.Player.cpp @@ -5,34 +5,201 @@ #include #include #include - +#include +#include #include #include +typedef struct _TARGET_PADS +{ + uint8_t Pid; + PVIGEM_TARGET Pad; + SOCKADDR_IN Addr; + time_t Utime; + uint8_t hMac[6]; +} TARGET_PADS, * PTARGET_PADS; + +#define PAD_SERVER_SIZE 5 +#define PAD_CLIENT_SIZE 24 +#define MAC_SIZE 6 + + +#define PAD_ONLINE 0x00u +#define PAD_VIBRA 0x01u +#define PAD_REG 0x03u +#define PAD_STATE 0x04u +#define PAD_RESET 0xFFu + SOCKET sock; -PVIGEM_TARGET pads[XUSER_MAX_COUNT]; +TARGET_PADS pads[XUSER_MAX_COUNT]; PVIGEM_CLIENT client; -void ResetGamepads() +bool checkvalidmac(uint8_t hMac[]) +{ + for (int i = 0; i < MAC_SIZE; i++) { + if (hMac[i] > uint8_t(0x00u) && hMac[i] < uint8_t(0xFFu)) + return true; + } + return false; +} + +VOID CALLBACK gamepad_callback(PVIGEM_CLIENT Client, PVIGEM_TARGET Target, UCHAR LargeMotor, UCHAR SmallMotor, UCHAR LedNumber, LPVOID UserData) { + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (Target == pads[i].Pad) { + + char packet[PAD_SERVER_SIZE]; + packet[0] = PAD_VIBRA; + packet[1] = pads[i].Pid; + + packet[2] = LargeMotor; + packet[3] = SmallMotor; + packet[4] = LedNumber; + + if (sendto(sock, (const char*)&packet, sizeof(packet), 0, (struct sockaddr*)&pads[i].Addr, sizeof(pads[i].Addr)) != sizeof(packet)) + printf("Failed to Send Notify message \n"); + break; + + } + } +} + +void CheckPadUpdateTimeOut() { + + while (true) + { + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (pads[i].Pad == nullptr) + continue; + + if (time(NULL) - pads[i].Utime >= 15) + { + printf("Server controller %d unregistered. \n", pads[i].Pid); + auto pad = pads[i].Pad; + pads[i].Pad = nullptr; + vigem_target_remove(client, pad); + vigem_target_x360_unregister_notification(pad); + vigem_target_free(pad); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + } +} + +void SetGamepadOnline(SOCKADDR_IN ClientAddr, uint8_t Pid, uint8_t Pindex) { + + char packet[PAD_SERVER_SIZE]; + + packet[0] = PAD_ONLINE; + packet[1] = Pid; + packet[2] = 0; + packet[3] = 0; + packet[4] = Pindex; + + if (sendto(sock, (const char*)&packet, sizeof(packet), 0, (struct sockaddr*)&ClientAddr, sizeof(ClientAddr)) != sizeof(packet)) + printf("Failed to Send Online message \n"); +} + +bool CheckMac(uint8_t iMac[], uint8_t hMac[]) { + + for (int i = 0; i < MAC_SIZE; i++) { + if (iMac[i] != hMac[i]) { + return false; + } + } + return true; +} + + + + +int GetNewPadIndex(uint8_t pid, SOCKADDR_IN ClientAddr,uint8_t hMac[]) +{ + int index = -1; + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (pads[i].Pad == nullptr && index < 0 ) + { + index = i; + + }else if ( pads[i].Pid == pid && CheckMac(pads[i].hMac,hMac)) + { + if (pads[i].Addr.sin_port!= ClientAddr.sin_port || pads[i].Addr.sin_addr.S_un.S_addr != ClientAddr.sin_addr.S_un.S_addr) + pads[i].Addr = ClientAddr; + index = i; + break; + } + } + return index; +} + + +int CheckPadIndex(uint8_t pid, SOCKADDR_IN ClientAddr, uint8_t hMac[]) +{ + int index = -1; + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (pads[i].Pad == nullptr) + continue; + + if (pads[i].Pid == pid && CheckMac(pads[i].hMac, hMac)) + { + if (pads[i].Addr.sin_port != ClientAddr.sin_port || pads[i].Addr.sin_addr.S_un.S_addr != ClientAddr.sin_addr.S_un.S_addr) + pads[i].Addr = ClientAddr; + index = i; + break; + } + } + return index; +} + +void ResetGamepad(SOCKADDR_IN ClientAddr, uint8_t hMac[]) { for (int i = 0; i < XUSER_MAX_COUNT; i++) { - auto pad = pads[i]; - pads[i] = nullptr; + if (pads[i].Pad == nullptr) + continue; + if (CheckMac(pads[i].hMac, hMac)) + { + printf("Server controller %d unregistered. \n", pads[i].Pid); + auto pad = pads[i].Pad; + pads[i].Pad = nullptr; + vigem_target_remove(client, pad); + vigem_target_x360_unregister_notification(pad); + vigem_target_free(pad); + } + } +} - if (pad == nullptr) +void ResetGamepads() +{ + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (pads[i].Pad == nullptr) continue; + auto pad = pads[i].Pad; + pads[i].Pad = nullptr; - vigem_target_remove(client, pad); - vigem_target_free(pad); + vigem_target_remove(client, pad); + vigem_target_x360_unregister_notification(pad); + vigem_target_free(pad); } } -int main() + +int main(int argc, char* argv[]) { - CoInitialize(NULL); + int port = 4313; - printf("Starting networking...\n"); + if (argc > 1) { + sscanf_s(argv[1], "%d", &port); + } + + CoInitialize(NULL); + + printf("Starting networking ...\n"); WSADATA wsaData; int wsaStartupResult = WSAStartup(MAKEWORD(2, 2), &wsaData); @@ -51,18 +218,18 @@ int main() struct sockaddr_in addr; addr.sin_family = AF_INET; - addr.sin_port = htons(4313); + addr.sin_port = htons(port); inet_pton(AF_INET, "0.0.0.0", &(addr.sin_addr)); if (bind(sock, (const sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { - printf("Failed to bind to 0.0.0.0:4313."); + printf("Failed to bind to 0.0.0.0:%d.", port); closesocket(sock); WSACleanup(); return -3; } - printf("Done.\n"); + printf("Bind to 0.0.0.0:%d .\n",port); printf("Setting up ViGEmClient...\n"); client = vigem_alloc(); @@ -85,50 +252,86 @@ int main() return -5; } - printf("Done.\n"); - printf("Waiting for data...\n"); + std::thread checktimeout(CheckPadUpdateTimeOut); + + uint8_t packet[PAD_CLIENT_SIZE]; struct sockaddr_in clientAddr; - uint8_t packet[sizeof(XINPUT_STATE) + 1]; + while (true) { - if (GetKeyState(VK_ESCAPE) & 0x8000) - break; - memset(&clientAddr, 0, sizeof(clientAddr)); - int addrLen = sizeof(clientAddr); - int bytesReceived = recvfrom(sock, (char*)&packet, sizeof(packet), 0, (struct sockaddr*)&clientAddr, &addrLen); + int addr_len = sizeof(clientAddr); + int bytesReceived = recvfrom(sock, (char*)&packet, sizeof(packet), 0, (struct sockaddr*)&clientAddr, &addr_len); - if (bytesReceived == 1 && packet[0] == (uint8_t)0xFFu) + if (bytesReceived >2 && packet[0] == (uint8_t)PAD_RESET) { - printf("Reset signal received, gamepads will be reset.\n"); - ResetGamepads(); - } - else if (bytesReceived == sizeof(packet) && packet[0] >= 0 && packet[0] < XUSER_MAX_COUNT) + printf("Reset signal received, controllers will be reset.\n"); + uint8_t hMac[MAC_SIZE]; + memcpy(hMac, packet + 1, MAC_SIZE); + ResetGamepad(clientAddr, hMac); + + }else if (bytesReceived > 2 && packet[0] == (uint8_t)PAD_REG) { - uint32_t i = (uint32_t)packet[0]; - XINPUT_STATE* state = (XINPUT_STATE*)(packet + 1); + uint8_t hMac[MAC_SIZE]; + uint8_t Pid = (uint8_t)packet[1]; + memcpy(hMac, packet+2, MAC_SIZE); + uint8_t pindex = GetNewPadIndex(Pid, clientAddr,hMac); - if (pads[i] == nullptr) + if (pindex >= 0 && pindex < XUSER_MAX_COUNT && checkvalidmac(hMac)) { - auto pad = vigem_target_x360_alloc(); - const auto targetAddResult = vigem_target_add(client, pad); - if (VIGEM_SUCCESS(targetAddResult)) - { - printf("Connected new gamepad %u.\n", i); - pads[i] = pad; - } - else - { - vigem_target_free(pad); - printf("Failed to add pad %u with error code 0x%08X.\n", i, targetAddResult); + if (pads[pindex].Pad == nullptr) { + auto pad = vigem_target_x360_alloc(); + const auto targetAddResult = vigem_target_add(client, pad); + if (VIGEM_SUCCESS(targetAddResult)) + { + printf("Connected server controller %u from client controller %u.\n", pindex, pads[pindex].Pid); + + pads[pindex].Pad = pad; + pads[pindex].Addr = clientAddr; + pads[pindex].Pid = Pid; + pads[pindex].Utime = time(NULL); + memcpy(pads[pindex].hMac,&hMac, MAC_SIZE); + + SetGamepadOnline(clientAddr,Pid, pindex); + + const auto retval = vigem_target_x360_register_notification(client, pad, &gamepad_callback, nullptr); + + if (VIGEM_SUCCESS(retval)) + { + printf("Server controller %d register for notification Success! \n", pads[pindex].Pid); + + } + else { + printf("Server controller %d register for notification failed! \n", pads[pindex].Pid); + vigem_target_x360_unregister_notification(pad); + } + + } + else + { + vigem_target_free(pad); + printf("Failed to add pad %u with error code 0x%08X.\n", pindex, targetAddResult); + } } - } - auto pad = pads[i]; - if (pad != nullptr) - vigem_target_x360_update(client, pad, *reinterpret_cast(&state->Gamepad)); + if (pads[pindex].Pad != nullptr) + pads[pindex].Utime = time(NULL); + } + } + else if (bytesReceived > 2 && packet[0] == (uint8_t)PAD_STATE && packet[1] >= 0 && packet[1] < XUSER_MAX_COUNT) + { + uint8_t hMac[MAC_SIZE]; + uint8_t Pid = (uint8_t)packet[1]; + memcpy(hMac, packet + 2, MAC_SIZE); + uint8_t pindex = CheckPadIndex(Pid, clientAddr, hMac); + XINPUT_STATE* state = (XINPUT_STATE*)(packet + MAC_SIZE + 2); + if (pindex >= 0 && pindex < XUSER_MAX_COUNT && pads[pindex].Pad != nullptr && checkvalidmac(hMac)) + { + pads[pindex].Utime = time(NULL); + vigem_target_x360_update(client, pads[pindex].Pad, *reinterpret_cast(&state->Gamepad)); + } } } diff --git a/README.md b/README.md index 7d832ee..316ee8d 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,36 @@ The Player will see if a gamepad with that index is connected. If not, a new vir ## Requirements - ViGEmBus https://github.com/ViGEm/ViGEmBus/releases + +# Thank usertoroot +# 1.13 By QeeAI + +2023.2.2 +1. Server (Custom Port) : netinput.paly.exe 0-65536 (Default 4313) +2. Client (Custom IPV4/Port) : netinput.capture.exe 192.168.1.31 (for example) 0-65536 (Default 4313) , "target.txt" never use . +3. Support Xbox360/X1S/XSS Vibration +4. GamePads depended By Client IP ,Port, and Local Xbox controller Index Id 。 +5. Added UDP Client HeartBreak every 5s, TimeOut 15s to break. + +2023年2月2日 +1. 服务端 (自定义端口) : netinput.paly.exe 4313(0-65536) +2. 客户端 (自定义IP和端口) : netinput.capture.exe 192.168.1.31(自定义) 4313(0-65536) , target.txt 不再使用。 +3. 现在可以完美支持Xbox360/X1S/XSS手柄震动。 +4. 服务端的手柄分组现在按IP,端口和本地XBOX手柄序号共同决定。 +5. 增加客户端手柄的心跳连接每5秒1次,超时15秒自动断开。 + + +# 1.14 By QeeAI +2023.2.4 +GamePads depended By Client Mac Address and Xbox controller Index Id now. + +2023年2月4日 +服务端的手柄分组现在按客户端MAC地址和XBOX手柄序号共同决定。 + + +# 1.15 By QeeAI +2023.2.5 +Added check valid for client mac address. + +2023年2月5日 +增加客户端MAC地址的有效性校验。 \ No newline at end of file