mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-06 13:47:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#include "ControllerBuffer.h"
|
||||
|
||||
#include "v8datamodel/DataModel.h"
|
||||
#include "v8datamodel/GamepadService.h"
|
||||
#include "XboxGameController.h"
|
||||
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace RBX;
|
||||
|
||||
#define INPUT_POLLING_HZ 90.0f
|
||||
|
||||
ControllerBuffer::ControllerBuffer() :
|
||||
xboxGameController(NULL)
|
||||
{}
|
||||
|
||||
void ControllerBuffer::updateBuffer()
|
||||
{
|
||||
if (xboxGameController == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boost::mutex::scoped_lock lock(changeInputBufferMutex);
|
||||
|
||||
IVectorView<IGamepad^>^ gamepads = Windows::Xbox::Input::Gamepad::Gamepads;
|
||||
|
||||
for ( unsigned int i = 0; i < gamepads->Size; ++i )
|
||||
{
|
||||
int rbxGamepadInt = xboxGameController->getRbxGamepadIntFromGamepad(gamepads->GetAt(i));
|
||||
|
||||
if (rbxGamepadInt >= 0)
|
||||
{
|
||||
IGamepadReading^ reading = gamepads->GetAt(i)->GetCurrentReading();
|
||||
|
||||
bufferGamepadButtons(rbxGamepadInt, reading);
|
||||
bufferGamepadAxis(rbxGamepadInt, reading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerBuffer::processButton(InputObject::UserInputType userInputType, const RBX::KeyCode buttonCode, const int buttonState)
|
||||
{
|
||||
if (buttonCode == SDLK_UNKNOWN || userInputType == InputObject::TYPE_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InputObject::UserInputState newState = (buttonState == 1) ? InputObject::INPUT_STATE_BEGIN : InputObject::INPUT_STATE_END;
|
||||
GamepadValue gamepadButtonCombo(userInputType, buttonCode);
|
||||
GamepadValueState newGamepadButtonState(Vector3(0,0,buttonState), newState);
|
||||
|
||||
if (bufferedInput.find(gamepadButtonCombo) == bufferedInput.end() ||
|
||||
bufferedInput[gamepadButtonCombo].back().first != newGamepadButtonState.first)
|
||||
{
|
||||
bufferedInput[gamepadButtonCombo].push_back(newGamepadButtonState);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerBuffer::bufferGamepadButtons(const int controllerIndex, IGamepadReading^& gamepadReading)
|
||||
{
|
||||
InputObject::UserInputType gamepadType = GamepadService::getGamepadEnumForInt(controllerIndex);
|
||||
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONA, gamepadReading->IsAPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONB, gamepadReading->IsBPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONX, gamepadReading->IsXPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONY, gamepadReading->IsYPressed ? 1 : 0);
|
||||
|
||||
processButton(gamepadType, SDLK_GAMEPAD_DPADLEFT, gamepadReading->IsDPadLeftPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_DPADRIGHT, gamepadReading->IsDPadRightPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_DPADUP, gamepadReading->IsDPadUpPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_DPADDOWN, gamepadReading->IsDPadDownPressed ? 1 : 0);
|
||||
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONL1, gamepadReading->IsLeftShoulderPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONR1, gamepadReading->IsRightShoulderPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONL3, gamepadReading->IsLeftThumbstickPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONR3, gamepadReading->IsRightThumbstickPressed ? 1 : 0);
|
||||
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONSTART, gamepadReading->IsMenuPressed ? 1 : 0);
|
||||
processButton(gamepadType, SDLK_GAMEPAD_BUTTONSELECT, gamepadReading->IsViewPressed ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
void ControllerBuffer::processAxis(RBX::InputObject::UserInputType userInputType, const RBX::KeyCode axisCode, const Vector3 axisValue)
|
||||
{
|
||||
if (axisCode == SDLK_UNKNOWN || userInputType == InputObject::TYPE_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RBX::InputObject::UserInputState newState = RBX::InputObject::INPUT_STATE_CHANGE;
|
||||
if (axisValue == G3D::Vector3::zero())
|
||||
{
|
||||
newState = RBX::InputObject::INPUT_STATE_END;
|
||||
}
|
||||
else if (axisValue.z >= 1.0f)
|
||||
{
|
||||
newState = RBX::InputObject::INPUT_STATE_BEGIN;
|
||||
}
|
||||
|
||||
GamepadValue gamepadAxisCombo(userInputType, axisCode);
|
||||
GamepadValueState newGamepadAxisState(axisValue, newState);
|
||||
|
||||
if (bufferedInput.find(gamepadAxisCombo) == bufferedInput.end() ||
|
||||
bufferedInput[gamepadAxisCombo].back().first != newGamepadAxisState.first)
|
||||
{
|
||||
bufferedInput[gamepadAxisCombo].push_back(newGamepadAxisState);
|
||||
}
|
||||
}
|
||||
void ControllerBuffer::bufferGamepadAxis(const int controllerIndex, IGamepadReading^& gamepadReading)
|
||||
{
|
||||
InputObject::UserInputType gamepadType = GamepadService::getGamepadEnumForInt(controllerIndex);
|
||||
|
||||
processAxis(gamepadType, SDLK_GAMEPAD_THUMBSTICK1, Vector3(gamepadReading->LeftThumbstickX, gamepadReading->LeftThumbstickY, 0));
|
||||
processAxis(gamepadType, SDLK_GAMEPAD_THUMBSTICK2, Vector3(gamepadReading->RightThumbstickX, gamepadReading->RightThumbstickY, 0));
|
||||
|
||||
processAxis(gamepadType, SDLK_GAMEPAD_BUTTONL2, Vector3(0, 0, gamepadReading->LeftTrigger));
|
||||
processAxis(gamepadType, SDLK_GAMEPAD_BUTTONR2, Vector3(0, 0, gamepadReading->RightTrigger));
|
||||
}
|
||||
|
||||
|
||||
ControllerBuffer::GamepadValueBufferMap ControllerBuffer::getBufferedInput()
|
||||
{
|
||||
GamepadValueBufferMap tmpMap;
|
||||
{
|
||||
boost::mutex::scoped_lock lock(changeInputBufferMutex);
|
||||
bufferedInput.swap(tmpMap);
|
||||
}
|
||||
|
||||
return tmpMap;
|
||||
}
|
||||
|
||||
float ControllerBuffer::getPollingMsec()
|
||||
{
|
||||
return (1.0f/INPUT_POLLING_HZ) * 1000.0f;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "rbx/rbxTime.h"
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "v8datamodel/InputObject.h"
|
||||
|
||||
using namespace Windows::Xbox::Input;
|
||||
|
||||
class XboxGameController;
|
||||
|
||||
class ControllerBuffer
|
||||
{
|
||||
public:
|
||||
typedef std::pair<RBX::InputObject::UserInputType, RBX::KeyCode> GamepadValue;
|
||||
typedef std::pair<RBX::Vector3, RBX::InputObject::UserInputState> GamepadValueState;
|
||||
typedef std::vector<GamepadValueState> GamepadValueStateHistoryVector;
|
||||
typedef boost::unordered_map<GamepadValue, GamepadValueStateHistoryVector> GamepadValueBufferMap;
|
||||
private:
|
||||
GamepadValueBufferMap bufferedInput;
|
||||
boost::mutex changeInputBufferMutex;
|
||||
|
||||
XboxGameController* xboxGameController;
|
||||
|
||||
void bufferGamepadButtons(const int controllerIndex, IGamepadReading^& gamepadReading);
|
||||
void bufferGamepadAxis(const int controllerIndex, IGamepadReading^& gamepadReading);
|
||||
|
||||
void processButton(RBX::InputObject::UserInputType userInputType, const RBX::KeyCode buttonCode, const int buttonState);
|
||||
void processAxis(RBX::InputObject::UserInputType userInputType, const RBX::KeyCode axisCode, const RBX::Vector3 axisValue);
|
||||
|
||||
public:
|
||||
ControllerBuffer();
|
||||
|
||||
void setXboxGameController(XboxGameController* newController) {xboxGameController = newController;}
|
||||
|
||||
void updateBuffer();
|
||||
float getPollingMsec();
|
||||
|
||||
GamepadValueBufferMap getBufferedInput();
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
?
|
||||
Binary file not shown.
@@ -0,0 +1,152 @@
|
||||
#include "KeyboardProvider.h"
|
||||
|
||||
#include "XboxService.h"
|
||||
#include "XboxUtils.h"
|
||||
#include "async.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "v8datamodel/TextBox.h"
|
||||
|
||||
using namespace Windows::Xbox::UI;
|
||||
using namespace RBX;
|
||||
|
||||
volatile long KeyboardProvider::keyboardOn = 0;
|
||||
|
||||
void KeyboardProvider::registerTextBoxListener(RBX::DataModel* dm)
|
||||
{
|
||||
if (auto uis = RBX::ServiceProvider::create<UserInputService>(dm))
|
||||
{
|
||||
showKeyboardSignal = uis->textBoxGainFocus.connect(boost::bind(&KeyboardProvider::showKeyBoard, this, _1));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardProvider::showKeyBoard(std::string& title, std::string& description, const std::string& defaultText, RBX::XboxKeyBoardType keyboardType, std::function<void(Platform::String^)>& successLambda, std::function<void(void)>& cancelLambda)
|
||||
{
|
||||
if( InterlockedCompareExchange(&keyboardOn, 1, 0 ) )
|
||||
return;
|
||||
|
||||
|
||||
VirtualKeyboardInputScope inputScope = VirtualKeyboardInputScope::Default;
|
||||
|
||||
switch (keyboardType)
|
||||
{
|
||||
case RBX::xbKeyBoard_Default:
|
||||
inputScope = VirtualKeyboardInputScope::Default;
|
||||
break;
|
||||
case RBX::xbKeyBoard_EmailSmtpAddress:
|
||||
inputScope = VirtualKeyboardInputScope::EmailSmtpAddress;
|
||||
break;
|
||||
case RBX::xbKeyBoard_Number:
|
||||
inputScope = VirtualKeyboardInputScope::Number;
|
||||
break;
|
||||
case RBX::xbKeyBoard_Password:
|
||||
inputScope = VirtualKeyboardInputScope::Password;
|
||||
break;
|
||||
case RBX::xbKeyBoard_Search:
|
||||
inputScope = VirtualKeyboardInputScope::Search;
|
||||
break;
|
||||
case RBX::xbKeyBoard_TelephoneNumber:
|
||||
inputScope = VirtualKeyboardInputScope::TelephoneNumber;
|
||||
break;
|
||||
case RBX::xbKeyBoard_Url:
|
||||
inputScope = VirtualKeyboardInputScope::Url;
|
||||
break;
|
||||
default:
|
||||
RBXASSERT(false); // new type?
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
async(SystemUI::ShowVirtualKeyboardAsync(ref new Platform::String(s2ws(&defaultText).data()), ref new Platform::String(s2ws(&title).data()), ref new Platform::String(s2ws(&description).data()), inputScope))
|
||||
.complete(successLambda)
|
||||
.cancelled(cancelLambda)
|
||||
.error(cancelLambda)
|
||||
.detach();
|
||||
}
|
||||
catch(Platform::Exception^ e)
|
||||
{
|
||||
dprintf("ShowVirtualKeyboard exception: [%u] %S\n", (unsigned)e->HResult, e->Message->Data() );
|
||||
cancelLambda();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardProvider::showKeyBoardLua(std::string& title, std::string& description, std::string& defaultText, RBX::XboxKeyBoardType keyboardType, RBX::DataModel* dm)
|
||||
{
|
||||
std::function<void(void)> cancelLambda = [=]() -> void
|
||||
{
|
||||
dm->submitTask([=](...)
|
||||
{
|
||||
if(PlatformService* p = ServiceProvider::find<PlatformService>(dm))
|
||||
p->keyboardClosedSignal(defaultText.data());
|
||||
}
|
||||
, DataModelJob::Write);
|
||||
|
||||
InterlockedExchange(&keyboardOn, 0);
|
||||
};
|
||||
|
||||
std::function<void(Platform::String^ defaultText)> successLambda = [=](Platform::String^ defaultText) -> void
|
||||
{
|
||||
dm->submitTask([=](...)
|
||||
{
|
||||
std::string textBoxValue = ws2s(defaultText->Data());
|
||||
if(PlatformService* p = ServiceProvider::find<PlatformService>(dm))
|
||||
p->keyboardClosedSignal(textBoxValue.data());
|
||||
}
|
||||
, DataModelJob::Write);
|
||||
|
||||
InterlockedExchange(&keyboardOn, 0);
|
||||
};
|
||||
|
||||
showKeyBoard(title, description, defaultText, keyboardType, successLambda, cancelLambda);
|
||||
}
|
||||
|
||||
void KeyboardProvider::showKeyBoard(shared_ptr<RBX::Instance> instance)
|
||||
{
|
||||
shared_ptr<RBX::TextBox> textBox = RBX::Instance::fastSharedDynamicCast<RBX::TextBox>(instance);
|
||||
RBX::DataModel* dm = DataModel::get(instance.get());
|
||||
|
||||
if (dm && textBox)
|
||||
{
|
||||
std::string text = textBox->getText();
|
||||
std::wstring wstr(text.begin(), text.end());
|
||||
Platform::String^ defaultText = ref new Platform::String(wstr.c_str());
|
||||
|
||||
std::function<void(void)> cancelLambda = [=]() -> void
|
||||
{
|
||||
if (textBox)
|
||||
{
|
||||
dm->submitTask([=](...)
|
||||
{
|
||||
if (auto uis = RBX::ServiceProvider::create<UserInputService>(dm))
|
||||
uis->textboxDidFinishEditing(text.c_str(), false);
|
||||
}
|
||||
, DataModelJob::Write);
|
||||
}
|
||||
|
||||
InterlockedExchange(&keyboardOn, 0);
|
||||
};
|
||||
|
||||
std::function<void(Platform::String^ defaultText)> successLambda = [=](Platform::String^ defaultText) -> void
|
||||
{
|
||||
if (textBox)
|
||||
{
|
||||
std::string textBoxValue = ws2s(defaultText->Data());
|
||||
|
||||
dm->submitTask([=](...)
|
||||
{
|
||||
if (auto uis = RBX::ServiceProvider::create<UserInputService>(dm))
|
||||
uis->textboxDidFinishEditing(textBoxValue.c_str(), true);
|
||||
}
|
||||
, DataModelJob::Write);
|
||||
}
|
||||
|
||||
InterlockedExchange(&keyboardOn, 0);
|
||||
};
|
||||
|
||||
std::string title = "Text Entry";
|
||||
std::string desc = "Please input text";
|
||||
showKeyBoard(title, desc, textBox->getText(), RBX::xbKeyBoard_Default, successLambda, cancelLambda);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "V8Tree/Instance.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class DataModel;
|
||||
|
||||
enum XboxKeyBoardType;
|
||||
}
|
||||
|
||||
|
||||
class KeyboardProvider
|
||||
{
|
||||
public:
|
||||
void registerTextBoxListener(RBX::DataModel* dm);
|
||||
void showKeyBoard(shared_ptr<RBX::Instance> instance);
|
||||
void showKeyBoardLua(std::string& title, std::string& description, std::string& defaultText, RBX::XboxKeyBoardType keyboardType, RBX::DataModel* dm);
|
||||
private:
|
||||
void showKeyBoard(std::string& title, std::string& description, const std::string& defaultText, RBX::XboxKeyBoardType keyboardType, std::function<void(Platform::String^)>& successLambda, std::function<void(void)>& cancelLambda);
|
||||
|
||||
rbx::signals::scoped_connection showKeyboardSignal;
|
||||
|
||||
static volatile long keyboardOn;
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"EndPoints": [
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.sitetest3.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.sitetest3.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.sitetest2.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.sitetest2.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.sitetest1.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.sitetest1.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.gametest5.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.gametest5.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.gametest4.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.gametest4.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.gametest3.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.gametest3.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.gametest2.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.gametest2.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.gametest1.pizzaboxer.fun",
|
||||
"RelyingParty": "https://api.gametest1.pizzaboxer.fun/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "api.watrbx.wtf",
|
||||
"RelyingParty": "https://api.watrbx.wtf/",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "fqdn"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "*",
|
||||
"RelyingParty": "http://xboxlive.com",
|
||||
"TokenType": "JWT",
|
||||
"HostType": "ip"
|
||||
},
|
||||
{
|
||||
"Protocol": "https",
|
||||
"Host": "*",
|
||||
"HostType": "wildcard"
|
||||
},
|
||||
{
|
||||
"Protocol": "http",
|
||||
"Host": "*",
|
||||
"HostType": "wildcard"
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:mx="http://schemas.microsoft.com/appx/2013/xbox/manifest" IgnorableNamespaces="mx">
|
||||
|
||||
<!--- 'Name' and 'Version' appear in the package path created by deploy -->
|
||||
<Identity Name="roblox"
|
||||
Publisher="CN=ROBLOX, O=ROBLOX, L=San Mateo, S=California, C=US"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>ROBLOX</DisplayName>
|
||||
<PublisherDisplayName>ROBLOX</PublisherDisplayName>
|
||||
<Logo>img/roblox.png</Logo>
|
||||
<Description>Community Created Gaming. Limitless Possibilities.</Description>
|
||||
</Properties>
|
||||
|
||||
<Prerequisites>
|
||||
<OSMinVersion>6.2</OSMinVersion>
|
||||
<OSMaxVersionTested>6.2</OSMaxVersionTested>
|
||||
</Prerequisites>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="en-us"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
|
||||
<!--- Debug config -->
|
||||
<!--- 'Id' is in the aumid returned from deploy -->
|
||||
<!--- 'Executable' must match the .exe produced by the build -->
|
||||
<Application Id="RobloxClient0"
|
||||
Executable="XboxClient.exe"
|
||||
EntryPoint="XboxClient.App">
|
||||
<!--- all fields except DisplayName not yet implemented -->
|
||||
<VisualElements
|
||||
DisplayName="ROBLOX"
|
||||
Logo="img/logo208.png"
|
||||
SmallLogo="img/logo100.png"
|
||||
Description="Description"
|
||||
ForegroundText="dark"
|
||||
BackgroundColor="#000040">
|
||||
<DefaultTile WideLogo="img/robloxWide.png" ShortName ="ROBLOX" />
|
||||
|
||||
<SplashScreen Image="img/roblox.png" />
|
||||
</VisualElements>
|
||||
|
||||
<mx:Ratings Category="game">
|
||||
<mx:Rating>ESRB:E10</mx:Rating>
|
||||
</mx:Ratings>
|
||||
|
||||
<Extensions>
|
||||
|
||||
<mx:Extension Category="xbox.live">
|
||||
<mx:XboxLive
|
||||
TitleId="1465F7BC"
|
||||
PrimaryServiceConfigId="bab50100-e49a-4ce8-b16f-918d1465f7bc"
|
||||
RequireXboxLive="true" />
|
||||
</mx:Extension>
|
||||
<mx:Extension Category="windows.xbox.networking">
|
||||
<!-- NOTE: talk to Michal before you mess with this section. I'm dead serious. -->
|
||||
<mx:XboxNetworkingManifest>
|
||||
<mx:SocketDescriptions>
|
||||
<mx:SocketDescription Name="MultiplayerSocketUdp" SecureIpProtocol="Udp" BoundPort="8700"> <!-- particularly this part! -->
|
||||
<mx:AllowedUsages>
|
||||
<mx:SecureDeviceSocketUsage Type="Initiate" />
|
||||
<mx:SecureDeviceSocketUsage Type="Accept" />
|
||||
<mx:SecureDeviceSocketUsage Type="SendGameData" />
|
||||
<mx:SecureDeviceSocketUsage Type="ReceiveGameData" />
|
||||
<mx:SecureDeviceSocketUsage Type="SendChat" />
|
||||
<mx:SecureDeviceSocketUsage Type="ReceiveChat" />
|
||||
</mx:AllowedUsages>
|
||||
</mx:SocketDescription>
|
||||
</mx:SocketDescriptions>
|
||||
<mx:SecureDeviceAssociationTemplates>
|
||||
<mx:SecureDeviceAssociationTemplate Name="MultiplayerUdp" InitiatorSocketDescription="MultiplayerSocketUdp" AcceptorSocketDescription="MultiplayerSocketUdp" MultiplayerSessionRequirement="Required">
|
||||
<mx:AllowedUsages>
|
||||
<mx:SecureDeviceAssociationUsage Type="Default" />
|
||||
</mx:AllowedUsages>
|
||||
</mx:SecureDeviceAssociationTemplate>
|
||||
</mx:SecureDeviceAssociationTemplates>
|
||||
</mx:XboxNetworkingManifest>
|
||||
</mx:Extension>
|
||||
|
||||
</Extensions>
|
||||
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClientServer" />
|
||||
<mx:Capability Name="kinectAudio"/>
|
||||
<mx:Capability Name="kinectGamechat"/>
|
||||
</Capabilities>
|
||||
|
||||
<Extensions>
|
||||
<mx:PackageExtension Category="xbox.storage">
|
||||
<mx:Storage>
|
||||
<mx:PersistentLocalStorage SizeInMegabytes="4095"/>
|
||||
</mx:Storage>
|
||||
</mx:PackageExtension>
|
||||
|
||||
</Extensions>
|
||||
|
||||
</Package>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package>
|
||||
|
||||
<Chunk Id="999">
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="*.xml"/>
|
||||
<FileGroup DestinationPath="\img" SourcePath="img" Include="*.*"/>
|
||||
</Chunk>
|
||||
|
||||
<Chunk Id="1000">
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="XboxClient.exe"/>
|
||||
</Chunk>
|
||||
|
||||
<Chunk Id="1001">
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="fmod.dll"/>
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="Microsoft.Xbox.Services.dll"/>
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="Microsoft.Xbox.GameChat.dll"/>
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="Microsoft.Xbox.Samples.NetworkMesh.dll"/>
|
||||
</Chunk>
|
||||
|
||||
<Chunk Id="1002">
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="*.bin"/>
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="*.rbxl"/>
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="resources.pri"/>
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="fflags.json"/>
|
||||
</Chunk>
|
||||
|
||||
<PlaceHolder/>
|
||||
|
||||
<!--
|
||||
|
||||
|
||||
Every layout should contain a "padding chunk" with Id="1073741823".
|
||||
The actual chunk will contain a small, one-byte file
|
||||
-->
|
||||
<Chunk Id="1073741823" Marker="Launch">
|
||||
<FileGroup DestinationPath="\" SourcePath="." Include="Update.alignmentchunk"/>
|
||||
</Chunk>
|
||||
</Package>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
xbdel XS:\NSAL.json
|
||||
xbdel XS:\enforceNSAL
|
||||
@@ -0,0 +1,2 @@
|
||||
xbcp NSAL.json XS:\NSAL.json
|
||||
xbcp enforceNSAL XS:\enforceNSAL
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
mklink /D %~dp0\Durango\Layout\Image\Loose\content %~dp0\..\content
|
||||
mklink /D %~dp0\Durango\Layout\Image\Loose\PlatformContent %~dp0\..\PlatformContent
|
||||
mklink /D %~dp0\Durango\Layout\Image\Loose\shaders %~dp0\..\shaders
|
||||
|
||||
pause
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "UserTranslator.h"
|
||||
#include "util/Http.h"
|
||||
#include "v8xml/WebParser.h"
|
||||
#include "rbx/make_shared.h"
|
||||
|
||||
extern void dprintf( const char* fmt, ... );
|
||||
|
||||
static std::string constructJsonRequest(const UserIDTranslator::GametagSet& tags)
|
||||
{
|
||||
if (!tags.empty())
|
||||
{
|
||||
std::string requestIds = "{\"ids\":[\"";
|
||||
bool noComma = true;
|
||||
for(UserIDTranslator::GametagSet::const_iterator inputIt = tags.begin(); inputIt != tags.end(); ++inputIt)
|
||||
{
|
||||
if (noComma)
|
||||
noComma = false;
|
||||
else
|
||||
requestIds += "\",\"";
|
||||
requestIds += *inputIt;
|
||||
}
|
||||
requestIds += "\"]}";
|
||||
|
||||
return requestIds;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
UserIDTranslator::~UserIDTranslator()
|
||||
{
|
||||
for(EntryMap::iterator it = entryMap.begin(); it != entryMap.end(); ++it)
|
||||
{
|
||||
delete it->second;
|
||||
}
|
||||
entryMap.clear();
|
||||
}
|
||||
|
||||
void UserIDTranslator::get( OutputVector& result, const InputVector& inputs )
|
||||
{
|
||||
GametagSet gamerTagsToConvert;
|
||||
|
||||
if (1)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(requestMutex);
|
||||
result.clear();
|
||||
|
||||
for(InputVector::const_iterator inputIt = inputs.begin(); inputIt != inputs.end(); ++inputIt)
|
||||
{
|
||||
std::string xuid = std::string(*inputIt);
|
||||
Entry* entry = entryMap[xuid];
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
entry = entryMap[xuid] = new Entry(*inputIt);
|
||||
gamerTagsToConvert.insert(xuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (entry->getState())
|
||||
{
|
||||
case UserIDTranslator::UnknownUser: // lets try again
|
||||
case UserIDTranslator::Failed:
|
||||
gamerTagsToConvert.insert(xuid);
|
||||
entry->state.store((int)Waiting);
|
||||
break;
|
||||
case UserIDTranslator::Ready: // Horray! do nothing
|
||||
case UserIDTranslator::Waiting: // Still waiting for response, do notning
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gamerTagsToConvert.empty())
|
||||
{
|
||||
std::string request = constructJsonRequest(gamerTagsToConvert);
|
||||
|
||||
RBX::Http( apiUrl ).post(request, RBX::Http::kContentTypeApplicationJson, false,
|
||||
|
||||
[this, gamerTagsToConvert](std::string* response, std::exception* exception) mutable
|
||||
{
|
||||
if (exception)
|
||||
dprintf("%s\n", exception->what());
|
||||
|
||||
if (response)
|
||||
{
|
||||
shared_ptr<const RBX::Reflection::ValueTable> jsonResult(rbx::make_shared<const RBX::Reflection::ValueTable>());
|
||||
bool parseResult = RBX::WebParser::parseJSONTable(*response, jsonResult);
|
||||
|
||||
RBX::Reflection::ValueTable::const_iterator itUsers = jsonResult->find("Users");
|
||||
if (itUsers != jsonResult->end() && itUsers->second.isType<shared_ptr<const RBX::Reflection::ValueArray> >())
|
||||
{
|
||||
shared_ptr<const RBX::Reflection::ValueArray> entries = itUsers->second.cast<shared_ptr<const RBX::Reflection::ValueArray> >();
|
||||
|
||||
for(RBX::Reflection::ValueArray::const_iterator it = entries->begin(); it != entries->end(); ++it)
|
||||
{
|
||||
if(!it->isType<shared_ptr<const RBX::Reflection::ValueTable> >())
|
||||
continue;
|
||||
|
||||
shared_ptr<const RBX::Reflection::ValueTable> keyValueEntry = it->cast<shared_ptr<const RBX::Reflection::ValueTable> >();
|
||||
|
||||
RBX::Reflection::ValueTable::const_iterator itGamertag = keyValueEntry->find("Id");
|
||||
RBX::Reflection::ValueTable::const_iterator itUserId = keyValueEntry->find("UserId");
|
||||
|
||||
if(itGamertag == keyValueEntry->end() || itUserId == keyValueEntry->end())
|
||||
{
|
||||
dprintf("UserTranslator: problem decoding part of JSON response\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string gamertag = "";
|
||||
if (itGamertag->second.isString())
|
||||
{
|
||||
gamertag = itGamertag->second.cast<std::string>();
|
||||
gamerTagsToConvert.erase(gamertag);
|
||||
|
||||
EntryMap::const_iterator it = entryMap.find(gamertag);
|
||||
if( it != entryMap.end() )
|
||||
{
|
||||
Entry* entry = it->second;
|
||||
if (!itUserId->second.isNumber())
|
||||
{
|
||||
entry->state.store(UnknownUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->robloxUid = itUserId->second.cast<int>();
|
||||
entry->state.store(Ready);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set values of gamer tags that were expected to come, but was not send or received
|
||||
for(boost::unordered_set<std::string>::const_iterator inputIt = gamerTagsToConvert.begin(); inputIt != gamerTagsToConvert.end(); ++inputIt)
|
||||
{
|
||||
entryMap[*inputIt]->state.store(Failed);
|
||||
}
|
||||
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UserIDTranslator::waitForUIDs( const std::vector< const UserIDTranslator::Entry* >& uids )
|
||||
{
|
||||
for (int k=0;;k=0)
|
||||
{
|
||||
for (int j=0; j<uids.size(); ++j)
|
||||
{
|
||||
auto st = uids[j]->getState();
|
||||
k += st != UserIDTranslator::Waiting;
|
||||
}
|
||||
|
||||
if( k == uids.size() ) return;
|
||||
Sleep(5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include "boost\unordered_map.hpp"
|
||||
#include "boost/unordered/unordered_set.hpp"
|
||||
#include "rbx/threadsafe.h"
|
||||
|
||||
class UserIDTranslator
|
||||
{
|
||||
public:
|
||||
|
||||
enum ResultState
|
||||
{
|
||||
Waiting = 0,
|
||||
Ready,
|
||||
Failed,
|
||||
UnknownUser
|
||||
};
|
||||
|
||||
struct Entry
|
||||
{
|
||||
friend class UserIDTranslator;
|
||||
|
||||
std::string gamertag;
|
||||
int robloxUid;
|
||||
|
||||
ResultState getState() const { return (ResultState)state.load(); }
|
||||
|
||||
private:
|
||||
std::atomic<int> state;
|
||||
Entry():
|
||||
robloxUid(0),
|
||||
state(Waiting){}
|
||||
|
||||
Entry(const std::string& gamertag)
|
||||
: gamertag(gamertag)
|
||||
, state(Waiting) {}
|
||||
};
|
||||
|
||||
typedef std::vector<const Entry*> OutputVector;
|
||||
typedef std::vector<std::string> InputVector;
|
||||
typedef boost::unordered_set<std::string> GametagSet;
|
||||
|
||||
UserIDTranslator(const std::string& apiUrl_): apiUrl(apiUrl_) {}
|
||||
~UserIDTranslator();
|
||||
|
||||
void get( OutputVector& result, const InputVector& inputs );
|
||||
|
||||
static void waitForUIDs( const std::vector< const UserIDTranslator::Entry* >& uids );
|
||||
|
||||
private:
|
||||
|
||||
std::string apiUrl; // https://api.watrbx.wtf/xbox/translate
|
||||
typedef boost::unordered_map<std::string, Entry*> EntryMap;
|
||||
EntryMap entryMap;
|
||||
RBX::mutex requestMutex; // taken only during get()
|
||||
};
|
||||
@@ -0,0 +1,362 @@
|
||||
#include "VoiceChat.h"
|
||||
|
||||
#include "XboxUtils.h"
|
||||
#include "rbx/Debug.h"
|
||||
#include "async.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#if defined(RBX_PLATFORM_DURANGO)
|
||||
void dprintf( const char* fmt, ... );
|
||||
#endif
|
||||
|
||||
using namespace Microsoft::Xbox::Services::Multiplayer;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
VoiceChat::VoiceChat(unsigned char sessionConsoleId)
|
||||
: currentUser(nullptr)
|
||||
{
|
||||
chatManager = ref new Microsoft::Xbox::GameChat::ChatManager();
|
||||
chatManager->ChatSettings->AudioThreadPeriodInMilliseconds = 40;
|
||||
|
||||
bool dropOutOfOrderPackets = false;
|
||||
std::string whateverName = "whatever";
|
||||
|
||||
try
|
||||
{
|
||||
networkMesh = ref new Microsoft::Xbox::Samples::NetworkMesh::MeshManager(sessionConsoleId, "MultiplayerUdp", ref new Platform::String(s2ws(&whateverName).data()), dropOutOfOrderPackets);
|
||||
}
|
||||
catch (Platform::Exception^ ex)
|
||||
{
|
||||
HRESULT hr = ex->HResult;
|
||||
const WCHAR* ps = ex->Message->Data();
|
||||
std::string string = ws2s(ps);
|
||||
|
||||
dprintf("Cannot init network mesh: %s", string.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VoiceChat::~VoiceChat()
|
||||
{
|
||||
Windows::Xbox::System::User::AudioDeviceAdded -= onAudioAdded;
|
||||
Windows::Xbox::System::User::AudioDeviceAdded -= onAudioRemoved;
|
||||
|
||||
if( chatManager != nullptr )
|
||||
{
|
||||
chatManager->OnDebugMessage -= onDebugMessageEvtToken;
|
||||
chatManager->OnOutgoingChatPacketReady -= onOutgoingChatPacketReadyEvtToken;
|
||||
chatManager->OnCompareUniqueConsoleIdentifiers -= onCompareUniqueConsoleIdentifiersEvtToken;
|
||||
Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged -= onResourceAvailabilityChangedEvtToken;
|
||||
|
||||
chatManager = nullptr;
|
||||
}
|
||||
|
||||
if ( networkMesh != nullptr)
|
||||
{
|
||||
networkMesh->GetMeshPacketManager()->OnChatMessageReceived -= onChatMessageEvtToken;
|
||||
networkMesh->OnPostHandshake -= onMeshConnectionAddedEvtToken;
|
||||
networkMesh->OnDisconnected -= onDisconnectedEvtToken;
|
||||
|
||||
networkMesh->Shutdown();
|
||||
networkMesh = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::init()
|
||||
{
|
||||
boost::weak_ptr<VoiceChat> weakPtrToThis = shared_from_this();
|
||||
|
||||
if (networkMesh)
|
||||
{
|
||||
onChatMessageEvtToken = networkMesh->GetMeshPacketManager()->OnChatMessageReceived += ref new Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::MeshChatMessageReceivedEvent^>(
|
||||
[weakPtrToThis]( Platform::Object^, Microsoft::Xbox::Samples::NetworkMesh::MeshChatMessageReceivedEvent^ args )
|
||||
{
|
||||
Windows::Storage::Streams::IBuffer^ chatPacket = args->Buffer;
|
||||
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
sharedPtrToThis->onIncomingChatMessage(chatPacket, args->Sender->GetAssociation());
|
||||
});
|
||||
|
||||
onMeshConnectionAddedEvtToken = networkMesh->OnPostHandshake += ref new Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^>(
|
||||
[weakPtrToThis]( Platform::Object^, Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ args )
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
sharedPtrToThis->onMeshConnectionAdded(args);
|
||||
});
|
||||
|
||||
onDisconnectedEvtToken = networkMesh->OnDisconnected += ref new Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^>(
|
||||
[weakPtrToThis]( Platform::Object^, Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ args )
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
sharedPtrToThis->onMeshConnectionRemoved(args);
|
||||
});
|
||||
}
|
||||
|
||||
onOutgoingChatPacketReadyEvtToken = chatManager->OnOutgoingChatPacketReady += ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::ChatPacketEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args )
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
sharedPtrToThis->onOutgoingChatPacketReady(args);
|
||||
}
|
||||
});
|
||||
|
||||
onDebugMessageEvtToken = chatManager->OnDebugMessage += ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::DebugMessageEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args )
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
sharedPtrToThis->onDebugMessageReceived(args);
|
||||
});
|
||||
|
||||
onCompareUniqueConsoleIdentifiersEvtToken = chatManager->OnCompareUniqueConsoleIdentifiers += ref new Microsoft::Xbox::GameChat::CompareUniqueConsoleIdentifiersHandler(
|
||||
[weakPtrToThis] ( Platform::Object^ obj1, Platform::Object^ obj2 )
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ sda1 = dynamic_cast<Windows::Xbox::Networking::SecureDeviceAssociation^>(obj1);
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ sda2 = dynamic_cast<Windows::Xbox::Networking::SecureDeviceAssociation^>(obj2);
|
||||
|
||||
if( sda1 != nullptr && sda2 != nullptr && sda1->RemoteSecureDeviceAddress != nullptr && sda2->RemoteSecureDeviceAddress != nullptr && sda1->RemoteSecureDeviceAddress->Compare(sda2->RemoteSecureDeviceAddress) == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
onAudioAdded = Windows::Xbox::System::User::AudioDeviceAdded += ref new Windows::Foundation::EventHandler<Windows::Xbox::System::AudioDeviceAddedEventArgs^>(
|
||||
[weakPtrToThis] (Platform::Object^,Windows::Xbox::System:: AudioDeviceAddedEventArgs^ args)
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
if (args->AudioDevice->Sharing == Windows::Xbox::System::AudioDeviceSharing::Exclusive &&
|
||||
args->AudioDevice->DeviceType == Windows::Xbox::System::AudioDeviceType::Capture)
|
||||
{
|
||||
sharedPtrToThis->headsetConnected(args->User);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onAudioRemoved = Windows::Xbox::System::User::AudioDeviceRemoved += ref new Windows::Foundation::EventHandler<Windows::Xbox::System::AudioDeviceRemovedEventArgs^>(
|
||||
[weakPtrToThis, this] (Platform::Object^,Windows::Xbox::System:: AudioDeviceRemovedEventArgs^ args)
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
if (args->AudioDevice->Sharing == Windows::Xbox::System::AudioDeviceSharing::Exclusive &&
|
||||
args->AudioDevice->DeviceType == Windows::Xbox::System::AudioDeviceType::Capture)
|
||||
{
|
||||
sharedPtrToThis->headsetDisconnected();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Upon enter constrained mode, mute everyone.
|
||||
// Upon leaving constrained mode, unmute everyone who was previously muted.
|
||||
onResourceAvailabilityChangedEvtToken = Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged += ref new Windows::Foundation::EventHandler< Platform::Object^ >(
|
||||
[weakPtrToThis] (Platform::Object^, Platform::Object^ )
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
if (Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Constrained)
|
||||
sharedPtrToThis->muteAll();
|
||||
else if(Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Full)
|
||||
sharedPtrToThis->unmuteAll();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
void VoiceChat::onMeshConnectionAdded(Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ meshConnection)
|
||||
{
|
||||
if(chatManager != nullptr)
|
||||
chatManager->HandleNewRemoteConsole(meshConnection->GetAssociation());
|
||||
}
|
||||
|
||||
void VoiceChat::onMeshConnectionRemoved(Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ meshConnection)
|
||||
{
|
||||
if (chatManager != nullptr)
|
||||
{
|
||||
async(chatManager->RemoveRemoteConsoleAsync(meshConnection->GetAssociation())).detach();
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::multiplayerUserAdded(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member)
|
||||
{
|
||||
// not used in this implementation
|
||||
}
|
||||
|
||||
void VoiceChat::multiplayerUserRemoved(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member)
|
||||
{
|
||||
// not used in this implementation
|
||||
}
|
||||
|
||||
void VoiceChat::onOutgoingChatPacketReady(Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args)
|
||||
{
|
||||
if (networkMesh)
|
||||
{
|
||||
if (args->SendPacketToAllConnectedConsoles)
|
||||
{
|
||||
// Send the chat packet to all connected consoles
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^>^ meshMembers = networkMesh->GetConnectionsByType(Microsoft::Xbox::Samples::NetworkMesh::ConnectionStatus::PostHandshake);
|
||||
for(int i = 0; i < meshMembers->Size; i++)
|
||||
{
|
||||
Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ member = meshMembers->GetAt(i);
|
||||
// The simple network layer in this sample doesn't support SendInOrder, so we are ignoring that
|
||||
networkMesh->GetMeshPacketManager()->SendChatMessage(member->GetAssociation(), args->PacketBuffer, args->SendReliable);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ targetAssociation = dynamic_cast<Windows::Xbox::Networking::SecureDeviceAssociation^>(args->UniqueTargetConsoleIdentifier);
|
||||
if (targetAssociation != nullptr)
|
||||
{
|
||||
networkMesh->GetMeshPacketManager()->SendChatMessage(targetAssociation, args->PacketBuffer, args->SendReliable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::onIncomingChatMessage(Windows::Storage::Streams::IBuffer^ chatMessage, Platform::Object^ uniqueRemoteConsoleIdentifier)
|
||||
{
|
||||
if(chatManager != nullptr)
|
||||
{
|
||||
Microsoft::Xbox::GameChat::ChatMessageType chatMessageType = chatManager->ProcessIncomingChatMessage(chatMessage, uniqueRemoteConsoleIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::setMuteState(bool muted, Microsoft::Xbox::GameChat::ChatUser^ chatUser)
|
||||
{
|
||||
if (chatManager != nullptr && chatUser != nullptr)
|
||||
if (!muted)
|
||||
chatManager->UnmuteUserFromAllChannels(chatUser);
|
||||
else
|
||||
chatManager->MuteUserFromAllChannels(chatUser);
|
||||
}
|
||||
|
||||
void VoiceChat::muteAll()
|
||||
{
|
||||
if (chatManager != nullptr)
|
||||
chatManager->MuteAllUsersFromAllChannels();
|
||||
}
|
||||
|
||||
void VoiceChat::unmuteAll()
|
||||
{
|
||||
if (chatManager != nullptr)
|
||||
chatManager->UnmuteAllUsersFromAllChannels();
|
||||
}
|
||||
|
||||
void VoiceChat::onDebugMessageReceived(Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args)
|
||||
{
|
||||
dprintf("GAMECHAT debug: %s", ws2s(args->Message->Data()).c_str());
|
||||
}
|
||||
|
||||
void VoiceChat::headsetConnected(Windows::Xbox::System::User^ user)
|
||||
{
|
||||
addLocalUserToChannel(0, user);
|
||||
|
||||
if (chatManager && networkMesh)
|
||||
{
|
||||
// this will ignore all members that we didnt establish connection yet. Screw those guys right?
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^>^ meshMembers = networkMesh->GetConnectionsByType(Microsoft::Xbox::Samples::NetworkMesh::ConnectionStatus::PostHandshake);
|
||||
for(auto member : meshMembers)
|
||||
{
|
||||
chatManager->HandleNewRemoteConsole(member->GetAssociation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::headsetDisconnected()
|
||||
{
|
||||
localUserHasHeadset = false;
|
||||
removeLocalUserFromChannel();
|
||||
}
|
||||
|
||||
void VoiceChat::addLocalUserToChannel(uint8 channelIndex, Windows::Xbox::System::User^ user)
|
||||
{
|
||||
bool oldHeadserState = localUserHasHeadset;
|
||||
|
||||
localUserHasHeadset = false;
|
||||
if (user)
|
||||
for (auto audio : user->AudioDevices)
|
||||
if (audio->Sharing == Windows::Xbox::System::AudioDeviceSharing::Exclusive && audio->DeviceType == Windows::Xbox::System::AudioDeviceType::Capture)
|
||||
{
|
||||
localUserHasHeadset = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(chatManager != nullptr && (currentUser != user || oldHeadserState != localUserHasHeadset))
|
||||
{
|
||||
if (localUserHasHeadset)
|
||||
{
|
||||
async(chatManager->AddLocalUserToChatChannelAsync(channelIndex, user)).detach();
|
||||
currentUser = user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::removeLocalUserFromChannel()
|
||||
{
|
||||
if(chatManager != nullptr && currentUser != nullptr)
|
||||
{
|
||||
async(chatManager->RemoveLocalUserFromChatChannelAsync(0, currentUser)).detach();
|
||||
currentUser = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::addConnections(Windows::Foundation::Collections::IVectorView<MultiplayerSessionMember^>^ members)
|
||||
{
|
||||
if (chatManager && networkMesh)
|
||||
{
|
||||
std::map<Platform::String^, bool> deviceTokenSeenMap;
|
||||
for( auto member : members )
|
||||
{
|
||||
if (member->Status != MultiplayerSessionMemberStatus::Active || member->SecureDeviceAddressBase64->IsEmpty() || member->IsCurrentUser)
|
||||
continue;
|
||||
|
||||
if( deviceTokenSeenMap.find(member->DeviceToken) == deviceTokenSeenMap.end() )
|
||||
{
|
||||
deviceTokenSeenMap[member->DeviceToken] = true;
|
||||
|
||||
Windows::Xbox::Networking::SecureDeviceAddress^ memberSecureDeviceAddress = Windows::Xbox::Networking::SecureDeviceAddress::FromBase64String( member->SecureDeviceAddressBase64 );
|
||||
|
||||
if( networkMesh != nullptr )
|
||||
networkMesh->ConnectToAddress(memberSecureDeviceAddress, member->Gamertag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChat::removeConnections(Windows::Foundation::Collections::IVectorView<MultiplayerSessionMember^>^ members)
|
||||
{
|
||||
if (chatManager && networkMesh)
|
||||
{
|
||||
std::map<Platform::String^, bool> deviceTokenSeenMap;
|
||||
for( auto member : members )
|
||||
{
|
||||
if (member->Status != MultiplayerSessionMemberStatus::Active || member->SecureDeviceAddressBase64->IsEmpty() || member->IsCurrentUser)
|
||||
continue;
|
||||
|
||||
if( deviceTokenSeenMap.find(member->DeviceToken) == deviceTokenSeenMap.end() )
|
||||
{
|
||||
deviceTokenSeenMap[member->DeviceToken] = true;
|
||||
|
||||
Windows::Xbox::Networking::SecureDeviceAddress^ memberSecureDeviceAddress = Windows::Xbox::Networking::SecureDeviceAddress::FromBase64String( member->SecureDeviceAddressBase64 );
|
||||
|
||||
if( networkMesh != nullptr )
|
||||
networkMesh->DisconectFromAddress(memberSecureDeviceAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <collection.h>
|
||||
|
||||
#include "VoiceChatBase.h"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class VoiceChat : public boost::enable_shared_from_this<VoiceChat>, public VoiceChatBase
|
||||
{
|
||||
private:
|
||||
VoiceChat(unsigned char sessionConsoleId);
|
||||
public:
|
||||
static boost::shared_ptr<VoiceChatBase> create(unsigned char sessionConsoleId)
|
||||
{
|
||||
boost::shared_ptr<VoiceChat> sp(new VoiceChat(sessionConsoleId));
|
||||
sp->init();
|
||||
return sp;
|
||||
}
|
||||
virtual ~VoiceChat();
|
||||
|
||||
// local user
|
||||
|
||||
virtual void addLocalUserToChannel(uint8 channelIndex, Windows::Xbox::System::User^ user);
|
||||
virtual void removeLocalUserFromChannel();
|
||||
|
||||
// remote users
|
||||
virtual void addConnections(Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^>^ members);
|
||||
virtual void removeConnections(Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^>^ members);
|
||||
void onMeshConnectionAdded(Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ meshConnection);
|
||||
void onMeshConnectionRemoved(Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ meshConnection);
|
||||
|
||||
// remote users
|
||||
virtual void multiplayerUserAdded(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member);
|
||||
virtual void multiplayerUserRemoved(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member);
|
||||
|
||||
// receive/send
|
||||
void onIncomingChatMessage(Windows::Storage::Streams::IBuffer^ chatMessage, Platform::Object^ uniqueRemoteConsoleIdentifier);
|
||||
void onOutgoingChatPacketReady(Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args);
|
||||
void onDebugMessageReceived(Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args);
|
||||
|
||||
// muting
|
||||
virtual void setMuteState(bool muted, Microsoft::Xbox::GameChat::ChatUser^ chatUser);
|
||||
virtual void muteAll();
|
||||
virtual void unmuteAll();
|
||||
|
||||
private:
|
||||
|
||||
void init();
|
||||
|
||||
typedef Platform::Box<unsigned int> ConsoleId;
|
||||
|
||||
Microsoft::Xbox::GameChat::ChatManager^ chatManager;
|
||||
|
||||
Windows::Xbox::System::User^ currentUser;
|
||||
|
||||
void headsetConnected(Windows::Xbox::System::User^ user);
|
||||
void headsetDisconnected();
|
||||
bool localUserHasHeadset;
|
||||
|
||||
// This is sample that we are not supposed to use... YOLO :D
|
||||
Microsoft::Xbox::Samples::NetworkMesh::MeshManager^ networkMesh;
|
||||
|
||||
Windows::Foundation::EventRegistrationToken onDebugMessageEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onOutgoingChatPacketReadyEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onCompareUniqueConsoleIdentifiersEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onResourceAvailabilityChangedEvtToken;
|
||||
|
||||
Windows::Foundation::EventRegistrationToken onAudioAdded;
|
||||
Windows::Foundation::EventRegistrationToken onAudioRemoved;
|
||||
|
||||
Windows::Foundation::EventRegistrationToken onChatMessageEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onMeshConnectionAddedEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onDisconnectedEvtToken;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "v8datamodel/PlatformService.h"
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <collection.h>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class VoiceChatBase
|
||||
{
|
||||
|
||||
public:
|
||||
virtual ~VoiceChatBase(){}
|
||||
|
||||
enum TalkingDelta
|
||||
{
|
||||
TalkingChange_Start,
|
||||
TalkingChange_End,
|
||||
TalkingChange_NoChange
|
||||
};
|
||||
|
||||
// local user
|
||||
virtual void addLocalUserToChannel(uint8 channelIndex, Windows::Xbox::System::User^ user) = 0;
|
||||
virtual void removeLocalUserFromChannel() = 0;
|
||||
|
||||
// remote users
|
||||
virtual void addConnections(Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^>^ members) = 0;
|
||||
virtual void removeConnections(Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^>^ members) = 0;
|
||||
|
||||
// remote users
|
||||
virtual void multiplayerUserAdded(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member) = 0;
|
||||
virtual void multiplayerUserRemoved(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member) = 0;
|
||||
|
||||
// muting
|
||||
virtual void setMuteState(bool muted, Microsoft::Xbox::GameChat::ChatUser^ chatUser) = 0;
|
||||
virtual unsigned getMuteState(unsigned memberId) { return (unsigned)VoiceChatState::voiceChatState_Available; }
|
||||
virtual void muteAll() = 0;
|
||||
virtual void unmuteAll() = 0;
|
||||
|
||||
virtual void muteUser(unsigned memberId){}
|
||||
virtual void unmuteUser(unsigned memberId){}
|
||||
|
||||
virtual void updateMember(unsigned memberId, float distance, TalkingDelta* talkingDeltaOut){}
|
||||
private:
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
#include "VoiceChatMaxNet.h"
|
||||
|
||||
#include "XboxUtils.h"
|
||||
#include "rbx/Debug.h"
|
||||
#include "async.h"
|
||||
|
||||
#include "g3d/g3dmath.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#if defined(RBX_PLATFORM_DURANGO)
|
||||
void dprintf( const char* fmt, ... );
|
||||
#endif
|
||||
|
||||
using namespace Microsoft::Xbox::Services::Multiplayer;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
VoiceChatMaxNet::VoiceChatMaxNet(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ currentUser, XboxLiveContext^ xboxLiveContext)
|
||||
: xbLiveContext(xboxLiveContext)
|
||||
{
|
||||
chatManager = ref new Microsoft::Xbox::GameChat::ChatManager();
|
||||
chatManager->ChatSettings->AudioThreadPeriodInMilliseconds = 40;
|
||||
chatManager->ChatSettings->AudioThreadAffinityMask = 0xff;
|
||||
|
||||
network.reset(new Xp2p::Network(currentUser));
|
||||
}
|
||||
|
||||
VoiceChatMaxNet::~VoiceChatMaxNet()
|
||||
{
|
||||
Windows::Xbox::System::User::AudioDeviceAdded -= onAudioAdded;
|
||||
Windows::Xbox::System::User::AudioDeviceRemoved -= onAudioRemoved;
|
||||
|
||||
if( chatManager != nullptr )
|
||||
{
|
||||
chatManager->OnDebugMessage -= onDebugMessageEvtToken;
|
||||
chatManager->OnOutgoingChatPacketReady -= onOutgoingChatPacketReadyEvtToken;
|
||||
chatManager->OnCompareUniqueConsoleIdentifiers -= onCompareUniqueConsoleIdentifiersEvtToken;
|
||||
Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged -= onResourceAvailabilityChangedEvtToken;
|
||||
chatManager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::init()
|
||||
{
|
||||
boost::weak_ptr<VoiceChatMaxNet> weakPtrToThis = shared_from_this();
|
||||
|
||||
onOutgoingChatPacketReadyEvtToken = chatManager->OnOutgoingChatPacketReady += ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::ChatPacketEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args )
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
sharedPtrToThis->onOutgoingChatPacketReady(args);
|
||||
}
|
||||
});
|
||||
|
||||
onDebugMessageEvtToken = chatManager->OnDebugMessage += ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::DebugMessageEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args )
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
sharedPtrToThis->onDebugMessageReceived(args);
|
||||
});
|
||||
|
||||
onCompareUniqueConsoleIdentifiersEvtToken = chatManager->OnCompareUniqueConsoleIdentifiers += ref new Microsoft::Xbox::GameChat::CompareUniqueConsoleIdentifiersHandler(
|
||||
[weakPtrToThis] ( Platform::Object^ obj1, Platform::Object^ obj2 )
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
RBXASSERT(dynamic_cast<ConsoleId^>(obj1) != nullptr);
|
||||
RBXASSERT(dynamic_cast<ConsoleId^>(obj2) != nullptr);
|
||||
return (obj1->Equals(obj2));
|
||||
}
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
// Upon enter constrained mode, mute everyone.
|
||||
// Upon leaving constrained mode, unmute everyone who was previously muted.
|
||||
onResourceAvailabilityChangedEvtToken = Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged += ref new Windows::Foundation::EventHandler< Platform::Object^ >(
|
||||
[weakPtrToThis] (Platform::Object^, Platform::Object^ )
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
if (Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Constrained)
|
||||
sharedPtrToThis->muteAll();
|
||||
else if(Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Full)
|
||||
sharedPtrToThis->unmuteAll();
|
||||
|
||||
});
|
||||
|
||||
onAudioAdded = Windows::Xbox::System::User::AudioDeviceAdded += ref new Windows::Foundation::EventHandler<Windows::Xbox::System::AudioDeviceAddedEventArgs^>(
|
||||
[weakPtrToThis, this] (Platform::Object^,Windows::Xbox::System:: AudioDeviceAddedEventArgs^ args)
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
if (args->AudioDevice->Sharing == Windows::Xbox::System::AudioDeviceSharing::Exclusive && args->AudioDevice->DeviceType == Windows::Xbox::System::AudioDeviceType::Capture)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
sharedPtrToThis->subscribeToChatMessages_NoLock();
|
||||
sharedPtrToThis->headsetConnected(args->User);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
onAudioRemoved = Windows::Xbox::System::User::AudioDeviceRemoved += ref new Windows::Foundation::EventHandler<Windows::Xbox::System::AudioDeviceRemovedEventArgs^>(
|
||||
[weakPtrToThis, this] (Platform::Object^,Windows::Xbox::System:: AudioDeviceRemovedEventArgs^ args)
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
if (args->AudioDevice->Sharing == Windows::Xbox::System::AudioDeviceSharing::Exclusive && args->AudioDevice->DeviceType == Windows::Xbox::System::AudioDeviceType::Capture)
|
||||
sharedPtrToThis->unsubscribeToChatMessages_NoLock();
|
||||
}
|
||||
});
|
||||
|
||||
boost::thread([weakPtrToThis]()
|
||||
{
|
||||
while (boost::shared_ptr<VoiceChatMaxNet> sharedPtrToThis = weakPtrToThis.lock() )
|
||||
{
|
||||
sharedPtrToThis->receive();
|
||||
Sleep(30);
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::updateMember(unsigned memberId, float distance, TalkingDelta* talkingDeltaOut)
|
||||
{
|
||||
if (chatManager)
|
||||
for (auto user : chatManager->GetChatUsers())
|
||||
{
|
||||
Xp2p::PeerPtr peer = peers[memberId];
|
||||
if (user->UniqueConsoleIdentifier && peer.get())
|
||||
{
|
||||
ConsoleId^ conId = dynamic_cast<ConsoleId^>(user->UniqueConsoleIdentifier);
|
||||
TalkingState^ currentTalkState = dynamic_cast<TalkingState^>(peer->userObject);
|
||||
RBXASSERT(conId != nullptr && currentTalkState != nullptr);
|
||||
|
||||
bool isTalkingNow = user->NumberOfPendingAudioPacketsToPlay > 0;
|
||||
if (currentTalkState->Value != isTalkingNow)
|
||||
{
|
||||
*talkingDeltaOut = isTalkingNow ? TalkingChange_Start : TalkingChange_End;
|
||||
peer->userObject = ref new TalkingState(isTalkingNow);
|
||||
}
|
||||
else
|
||||
*talkingDeltaOut = TalkingChange_NoChange;
|
||||
|
||||
if (!user->IsMuted)
|
||||
{
|
||||
if (user->GetAllChannels()->Size > 0 && user->GetAllChannels()->GetAt(0) == 0)
|
||||
{
|
||||
ConsoleId^ conId = dynamic_cast<ConsoleId^>(user->UniqueConsoleIdentifier);
|
||||
if (conId != nullptr && (unsigned)conId->Value == memberId)
|
||||
{
|
||||
static const float fadeOutDistance = 100; // last distance[studs] that you hear fully
|
||||
static const float slopeInv = 160; // sounds decreases from fadeOutDistance to (fadeOutDistance + slope) to complete zero
|
||||
|
||||
user->Volume = G3D::clamp(((fadeOutDistance - distance) / slopeInv) + 1, 0, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
user->Volume = 1;
|
||||
}
|
||||
else
|
||||
user->Volume = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::handleNewRemoteConnection(ConsoleId^ consoleId)
|
||||
{
|
||||
chatManager->HandleNewRemoteConsole(consoleId);
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::multiplayerUserAdded(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
RBXASSERT(peers.find(member->MemberId) == peers.end());
|
||||
|
||||
if (peers.find(member->MemberId) == peers.end())
|
||||
{
|
||||
boost::weak_ptr<VoiceChatMaxNet> weakPtrToThis = shared_from_this();
|
||||
|
||||
if (member->Status == MultiplayerSessionMemberStatus::Active && !member->SecureDeviceAddressBase64->IsEmpty() && !member->IsCurrentUser)
|
||||
{
|
||||
Microsoft::Xbox::GameChat::ChatUser^ localUser;
|
||||
for(auto user : chatManager->GetChatUsers())
|
||||
if (user->IsLocal)
|
||||
{
|
||||
localUser = user;
|
||||
break;
|
||||
}
|
||||
|
||||
RBXASSERT(localUser);
|
||||
if (!localUser)
|
||||
return;
|
||||
|
||||
peers[member->MemberId] = network->createPeer(member,
|
||||
[weakPtrToThis, localUser, this](Xp2p::PeerPtr peer)
|
||||
{
|
||||
peer->userObject = ref new TalkingState(false);
|
||||
boost::shared_ptr<VoiceChatMaxNet> voiceChat = weakPtrToThis.lock();
|
||||
if (voiceChat != nullptr)
|
||||
{
|
||||
for (auto audio : localUser->User->AudioDevices)
|
||||
{
|
||||
if (audio->Sharing == Windows::Xbox::System::AudioDeviceSharing::Exclusive && audio->DeviceType == Windows::Xbox::System::AudioDeviceType::Capture)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
voiceChat->handleNewRemoteConnection(ref new ConsoleId(peer->getPeerId()));
|
||||
voiceChat->subscribeToChatMessages_NoLock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::multiplayerUserRemoved(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
RBXASSERT(peers.find(member->MemberId) != peers.end());
|
||||
|
||||
if (peers.find(member->MemberId) != peers.end())
|
||||
{
|
||||
network->destroyPeer(peers[member->MemberId]);
|
||||
peers.erase(member->MemberId);
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::sendData(Xp2p::PeerPtr peer, Windows::Storage::Streams::IBuffer^ buffer, unsigned int flags)
|
||||
{
|
||||
if (!peer) return;
|
||||
|
||||
VoicePacket packet;
|
||||
packet.type = PacketType_VoicePacket;
|
||||
packet.size = sizeof(Xp2p::PacketHeader) + buffer->Length;
|
||||
|
||||
ComPtr<IBufferByteAccess> bufferByteAccess;
|
||||
reinterpret_cast<IInspectable*>(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
|
||||
unsigned char* dataPtr;
|
||||
bufferByteAccess->Buffer(&dataPtr);
|
||||
memcpy(packet.data, dataPtr, buffer->Length);
|
||||
|
||||
Xp2p::NetResult result;
|
||||
if (peer)
|
||||
do
|
||||
{
|
||||
result = network->sendPacket(peer, &packet, 10, flags);
|
||||
if (result == Xp2p::Net_Retry)
|
||||
Sleep(1);
|
||||
}
|
||||
while(result == Xp2p::Net_Retry);
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::onOutgoingChatPacketReady(Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args)
|
||||
{
|
||||
if (network.get())
|
||||
{
|
||||
unsigned flags = Xp2p::Packet_Default;
|
||||
flags |= args->SendReliable ? Xp2p::Packet_Reliable : 0;
|
||||
flags |= args->SendInOrder ? Xp2p::Packet_Ordered : 0;
|
||||
|
||||
if (args->SendPacketToAllConnectedConsoles)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
for (auto peer : peers)
|
||||
{
|
||||
RBX::mutex::scoped_lock subscriptionLock(subscribedPeersLock);
|
||||
if (subscibedPeers.find(peer.first) != subscibedPeers.end())
|
||||
sendData(peer.second, args->PacketBuffer, flags);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
unsigned int memId = dynamic_cast<ConsoleId^>(args->UniqueTargetConsoleIdentifier)->Value;
|
||||
Xp2p::PeerPtr peer = peers[memId];
|
||||
sendData(peer, args->PacketBuffer, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::receive()
|
||||
{
|
||||
Xp2p::PacketStorage storage;
|
||||
Xp2p::NetResult res = network->recvPacket(&storage, 1);
|
||||
|
||||
if (res == Xp2p::Net_Ok)
|
||||
{
|
||||
Xp2p::PeerPtr peer;
|
||||
do
|
||||
{
|
||||
res = network->processPacket(peer, &storage);
|
||||
|
||||
if (res == Xp2p::Net_Ok)
|
||||
{
|
||||
switch (storage.type)
|
||||
{
|
||||
case PacketType_VoicePacket:
|
||||
{
|
||||
VoicePacket* packet = reinterpret_cast<VoicePacket*>(&storage);
|
||||
ConsoleId^ consoleId = ref new ConsoleId(peer->getPeerId());
|
||||
|
||||
DataWriter ^writer = ref new DataWriter();
|
||||
writer->WriteBytes(Platform::ArrayReference<unsigned char>(packet->data, packet->getDataSize()));
|
||||
IBuffer ^buffer = writer->DetachBuffer();
|
||||
|
||||
chatManager->ProcessIncomingChatMessage(buffer, consoleId);
|
||||
break;
|
||||
}
|
||||
case PacketType_ControlPacket:
|
||||
{
|
||||
ControlVoicePacket* packet = reinterpret_cast<ControlVoicePacket*>(&storage);
|
||||
ConsoleId^ consoleId = ref new ConsoleId(peer->getPeerId());
|
||||
|
||||
|
||||
if (packet->subscribe > 0)
|
||||
{
|
||||
if (Microsoft::Xbox::GameChat::ChatUser^ user = getUserByMemberId(peer->getPeerId()))
|
||||
{
|
||||
boost::weak_ptr<VoiceChatMaxNet> weakPtrToThis = shared_from_this();
|
||||
|
||||
async(xbLiveContext->PrivacyService->CheckPermissionWithTargetUserAsync(Microsoft::Xbox::Services::Privacy::PermissionIdConstants::CommunicateUsingVoice, user->XboxUserId)).complete(
|
||||
[weakPtrToThis, peer](Privacy::PermissionCheckResult^ result)
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sharedThis = weakPtrToThis.lock();
|
||||
if (sharedThis && result->IsAllowed)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(sharedThis->subscribedPeersLock);
|
||||
sharedThis->subscibedPeers.insert(peer->getPeerId());
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(subscribedPeersLock);
|
||||
subscibedPeers.erase(peer->getPeerId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
RBXASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (res == Xp2p::Net_Retry)
|
||||
Sleep(5);
|
||||
}
|
||||
while (res == Xp2p::Net_Retry);
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::setMuteState(bool muted, Microsoft::Xbox::GameChat::ChatUser^ chatUser)
|
||||
{
|
||||
if (chatManager != nullptr && chatUser != nullptr)
|
||||
if (!muted)
|
||||
chatManager->UnmuteUserFromAllChannels(chatUser);
|
||||
else
|
||||
chatManager->MuteUserFromAllChannels(chatUser);
|
||||
}
|
||||
|
||||
unsigned VoiceChatMaxNet::getMuteState(unsigned memberId)
|
||||
{
|
||||
if (Microsoft::Xbox::GameChat::ChatUser^ user = getUserByMemberId(memberId))
|
||||
{
|
||||
if (subscibedPeers.find(memberId) != subscibedPeers.end())
|
||||
return user->IsMuted ? VoiceChatState::voiceChatState_Available : VoiceChatState::voiceChatState_Muted;
|
||||
else
|
||||
return VoiceChatState::voiceChatState_NotInChat;
|
||||
}
|
||||
return VoiceChatState::voiceChatState_UnknownUser;
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::muteAll()
|
||||
{
|
||||
if (chatManager != nullptr)
|
||||
chatManager->MuteAllUsersFromAllChannels();
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::unmuteAll()
|
||||
{
|
||||
if (chatManager != nullptr)
|
||||
chatManager->UnmuteAllUsersFromAllChannels();
|
||||
}
|
||||
|
||||
Microsoft::Xbox::GameChat::ChatUser^ VoiceChatMaxNet::getUserByMemberId(unsigned id)
|
||||
{
|
||||
if (chatManager)
|
||||
{
|
||||
for (auto user : chatManager->GetChatUsers())
|
||||
{
|
||||
ConsoleId^ conId = dynamic_cast<ConsoleId^>(user->UniqueConsoleIdentifier);
|
||||
if (conId != nullptr && (unsigned)conId->Value == id)
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::muteUser(unsigned memberId)
|
||||
{
|
||||
if (Microsoft::Xbox::GameChat::ChatUser^ user = getUserByMemberId(memberId))
|
||||
chatManager->MuteUserFromAllChannels(user);
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::unmuteUser(unsigned memberId)
|
||||
{
|
||||
if (Microsoft::Xbox::GameChat::ChatUser^ user = getUserByMemberId(memberId))
|
||||
chatManager->UnmuteUserFromAllChannels(user);
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::onDebugMessageReceived(Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args)
|
||||
{
|
||||
dprintf("GAMECHAT debug: %s", ws2s(args->Message->Data()).c_str());
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::headsetConnected(Windows::Xbox::System::User^ user)
|
||||
{
|
||||
addLocalUserToChannel(currentChannel, user);
|
||||
|
||||
if (chatManager != nullptr)
|
||||
{
|
||||
// this will ignore all members that we didnt establish connection yet. Screw those guys right?
|
||||
RBX::mutex::scoped_lock lock(peersLock);
|
||||
for(auto peer : peers)
|
||||
{
|
||||
chatManager->HandleNewRemoteConsole(ref new ConsoleId(peer.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::addLocalUserToChannel(uint8 channelIndex, Windows::Xbox::System::User^ user)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(channelsLock);
|
||||
|
||||
if(chatManager != nullptr && (currentChannel != channelIndex || currentUser != user))
|
||||
{
|
||||
async(chatManager->AddLocalUserToChatChannelAsync(channelIndex, user))
|
||||
.except( [](Platform::Exception^ e) -> void
|
||||
{
|
||||
dprintf( "%s exception [0x%x]: %S\n", __FUNCTION__, (int)e->HResult, e->Message->Data() );
|
||||
})
|
||||
.join();
|
||||
|
||||
removeLocalUserFromChannel_NoLock();
|
||||
|
||||
currentChannel = channelIndex;
|
||||
currentUser = user;
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::removeLocalUserFromChannel()
|
||||
{
|
||||
RBX::mutex::scoped_lock lock(channelsLock);
|
||||
removeLocalUserFromChannel_NoLock();
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::removeLocalUserFromChannel_NoLock()
|
||||
{
|
||||
if(chatManager != nullptr && currentUser != nullptr && currentChannel >= 0 )
|
||||
{
|
||||
async(chatManager->RemoveLocalUserFromChatChannelAsync(currentChannel, currentUser))
|
||||
.except( [](Platform::Exception^ e) -> void
|
||||
{
|
||||
dprintf( "%s exception [0x%x]: %S\n", __FUNCTION__, (int)e->HResult, e->Message->Data() );
|
||||
}).join();
|
||||
|
||||
currentChannel = -1;
|
||||
currentUser = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::addConnections(Windows::Foundation::Collections::IVectorView<MultiplayerSessionMember^>^ members)
|
||||
{
|
||||
// we do something smarter in max net
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::removeConnections(Windows::Foundation::Collections::IVectorView<MultiplayerSessionMember^>^ members)
|
||||
{
|
||||
// we do something smarter in max net
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::unsubscribeToChatMessages_NoLock()
|
||||
{
|
||||
for (auto peer : peers)
|
||||
{
|
||||
sendVoiceControlPacket(peer.second, false);
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::subscribeToChatMessages_NoLock()
|
||||
{
|
||||
for (auto peer : peers)
|
||||
{
|
||||
sendVoiceControlPacket(peer.second, true);
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceChatMaxNet::sendVoiceControlPacket(Xp2p::PeerPtr peer, bool subscribe)
|
||||
{
|
||||
if (!peer) return;
|
||||
|
||||
ControlVoicePacket packet;
|
||||
packet.type = PacketType_ControlPacket;
|
||||
packet.size = sizeof(ControlVoicePacket);
|
||||
packet.subscribe = subscribe ? 1 : 0;
|
||||
|
||||
unsigned flags = Xp2p::Packet_Reliable;
|
||||
|
||||
Xp2p::NetResult result;
|
||||
if (peer)
|
||||
do
|
||||
{
|
||||
result = network->sendPacket(peer, &packet, 10, flags);
|
||||
if (result == Xp2p::Net_Retry)
|
||||
Sleep(1);
|
||||
}
|
||||
while(result == Xp2p::Net_Retry);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <collection.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <robuffer.h>
|
||||
#include <ppl.h>
|
||||
|
||||
#include "p2p.h"
|
||||
#include "rbx/threadsafe.h"
|
||||
#include "VoiceChatBase.h"
|
||||
|
||||
using namespace Microsoft::Xbox::Services;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
struct VoicePacket : public Xp2p::PacketHeader
|
||||
{
|
||||
unsigned int getDataSize()
|
||||
{
|
||||
return size - sizeof(Xp2p::PacketHeader);
|
||||
}
|
||||
|
||||
unsigned char data[Xp2p::PacketStorage::MaxPayloadSize];
|
||||
};
|
||||
|
||||
struct ControlVoicePacket : public Xp2p::PacketHeader
|
||||
{
|
||||
uint8 subscribe;
|
||||
};
|
||||
|
||||
class VoiceChatMaxNet : public boost::enable_shared_from_this<VoiceChatMaxNet>, public VoiceChatBase
|
||||
{
|
||||
private:
|
||||
VoiceChatMaxNet(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ currentUser, XboxLiveContext^ xboxLiveContext);
|
||||
|
||||
public:
|
||||
|
||||
static boost::shared_ptr<VoiceChatBase> create(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ currentUser, XboxLiveContext^ xboxLiveContext)
|
||||
{
|
||||
boost::shared_ptr<VoiceChatMaxNet> sp(new VoiceChatMaxNet(currentUser, xboxLiveContext));
|
||||
sp->init();
|
||||
return sp;
|
||||
}
|
||||
|
||||
virtual ~VoiceChatMaxNet();
|
||||
|
||||
typedef Platform::Box<unsigned int> ConsoleId;
|
||||
typedef Platform::Box<bool> TalkingState;
|
||||
|
||||
// local user
|
||||
void headsetConnected(Windows::Xbox::System::User^ user);
|
||||
virtual void addLocalUserToChannel(uint8 channelIndex, Windows::Xbox::System::User^ user);
|
||||
virtual void removeLocalUserFromChannel();
|
||||
|
||||
// remote users
|
||||
virtual void addConnections(Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^>^ members);
|
||||
virtual void removeConnections(Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^>^ members);
|
||||
|
||||
// remote users
|
||||
virtual void multiplayerUserAdded(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member);
|
||||
virtual void multiplayerUserRemoved(Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionMember^ member);
|
||||
|
||||
// receive/send
|
||||
void onOutgoingChatPacketReady(Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args);
|
||||
void onDebugMessageReceived(Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args);
|
||||
|
||||
// muting
|
||||
virtual void setMuteState(bool muted, Microsoft::Xbox::GameChat::ChatUser^ chatUser);
|
||||
virtual unsigned getMuteState(unsigned memberId);
|
||||
virtual void muteAll();
|
||||
virtual void unmuteAll();
|
||||
virtual void muteUser(unsigned memberId);
|
||||
virtual void unmuteUser(unsigned memberId);
|
||||
|
||||
virtual void updateMember(unsigned memberId, float distance, TalkingDelta* talkingDeltaOut);
|
||||
|
||||
void receive();
|
||||
void sendVoiceControlPacket(Xp2p::PeerPtr peer, bool subscribe);
|
||||
void unsubscribeToChatMessages_NoLock();
|
||||
void subscribeToChatMessages_NoLock();
|
||||
void handleNewRemoteConnection(ConsoleId^ consoleId);
|
||||
|
||||
private:
|
||||
|
||||
enum PacketType
|
||||
{
|
||||
PacketType_VoicePacket = 1,
|
||||
PacketType_ControlPacket = 2
|
||||
};
|
||||
|
||||
void init();
|
||||
void removeLocalUserFromChannel_NoLock();
|
||||
Microsoft::Xbox::GameChat::ChatUser^ getUserByMemberId(unsigned id);
|
||||
|
||||
Microsoft::Xbox::GameChat::ChatManager^ chatManager;
|
||||
|
||||
int currentChannel;
|
||||
Windows::Xbox::System::User^ currentUser;
|
||||
|
||||
void sendData(Xp2p::PeerPtr peer, Windows::Storage::Streams::IBuffer^ buffer, unsigned int flags);
|
||||
|
||||
boost::scoped_ptr<Xp2p::Network> network;
|
||||
XboxLiveContext^ xbLiveContext;
|
||||
|
||||
RBX::mutex channelsLock;
|
||||
RBX::mutex peersLock;
|
||||
RBX::mutex subscribedPeersLock;
|
||||
boost::unordered_map<unsigned int, Xp2p::PeerPtr> peers;
|
||||
boost::unordered_set<unsigned int> subscibedPeers;
|
||||
|
||||
Windows::Foundation::EventRegistrationToken onDebugMessageEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onOutgoingChatPacketReadyEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onCompareUniqueConsoleIdentifiersEvtToken;
|
||||
Windows::Foundation::EventRegistrationToken onResourceAvailabilityChangedEvtToken;
|
||||
|
||||
Windows::Foundation::EventRegistrationToken onAudioAdded;
|
||||
Windows::Foundation::EventRegistrationToken onAudioRemoved;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
|
||||
#include "XbLiveUtils.h"
|
||||
|
||||
|
||||
#include <collection.h>
|
||||
#include "xdpevents.h"
|
||||
#include <ppltasks.h>
|
||||
#include <map>
|
||||
|
||||
#include "Util/Http.h"
|
||||
#include "XboxUtils.h"
|
||||
#include "UserTranslator.h"
|
||||
#include "async.h"
|
||||
#include "rbx/make_shared.h"
|
||||
|
||||
using namespace Windows::Xbox::Input;
|
||||
using namespace Windows::Xbox::System;
|
||||
using namespace Windows::Xbox::UI;
|
||||
using namespace Windows::Xbox::ApplicationModel::Core;
|
||||
using namespace Windows::Xbox::Multiplayer;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
using namespace Microsoft::Xbox::Services;
|
||||
using namespace Microsoft::Xbox::Services::Presence;
|
||||
using namespace Microsoft::Xbox::Services::Social;
|
||||
using namespace Microsoft::Xbox::Services::Privacy;
|
||||
|
||||
#define TITLE_ID 0x1465F7BC
|
||||
#include <map>
|
||||
|
||||
|
||||
int getFriends(std::string* ret, UserIDTranslator* translator, Microsoft::Xbox::Services::XboxLiveContext^ xblContext)
|
||||
{
|
||||
bool comma = false;
|
||||
|
||||
if( !xblContext || !xblContext->User->IsSignedIn)
|
||||
{
|
||||
dprintf("User not signed in, friends are not available.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
XboxLiveContext^ xboxLiveContext = xblContext;
|
||||
|
||||
try{
|
||||
|
||||
std::vector< Platform::String^ > friendXuids;
|
||||
IVectorView< XboxUserProfile^ >^ friendProfiles = ref new Platform::Collections::VectorView< XboxUserProfile^ >(); // make sure we it's not null if an exception occurs
|
||||
IVectorView< PresenceRecord^ >^ friendPresence = ref new Platform::Collections::VectorView< PresenceRecord^ >();
|
||||
std::vector< std::string > friendGamertags;
|
||||
std::vector< const UserIDTranslator::Entry* > friendRobloxUids;
|
||||
|
||||
try {
|
||||
|
||||
// get all favorite people from player's list
|
||||
async( xboxLiveContext->SocialService->GetSocialRelationshipsAsync()).complete(
|
||||
[&friendXuids]( XboxSocialRelationshipResult^ result ) -> void
|
||||
{
|
||||
for ( auto r : result->Items )
|
||||
{
|
||||
if( r->IsFollowingCaller )
|
||||
friendXuids.push_back( r->XboxUserId );
|
||||
}
|
||||
}
|
||||
).except( [](Platform::Exception^ e) -> void
|
||||
{
|
||||
dprintf( "%s exception [0x%x]: %S\n", __FUNCTION__, (int)e->HResult, e->Message->Data() );
|
||||
}
|
||||
).join();
|
||||
|
||||
if( friendXuids.size() == 0)
|
||||
{
|
||||
dprintf("User has no friends.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get those people's profiles
|
||||
auto& a1 = async( xboxLiveContext->ProfileService->GetUserProfilesAsync( ref new Platform::Collections::VectorView< Platform::String^ > ( friendXuids ) ) ).complete(
|
||||
[&friendProfiles, &friendGamertags]( IVectorView< XboxUserProfile^ > ^ profiles ) -> void
|
||||
{
|
||||
friendProfiles = profiles;
|
||||
for (auto pr : profiles)
|
||||
{
|
||||
friendGamertags.push_back(ws2s(pr->Gamertag->Data()));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// get those people's presence
|
||||
auto& a2 = async( xboxLiveContext->PresenceService->GetPresenceForMultipleUsersAsync( ref new Platform::Collections::VectorView< Platform::String^ >( friendXuids ) ) ).complete(
|
||||
[&friendPresence]( IVectorView< PresenceRecord^ >^ pr )
|
||||
{
|
||||
friendPresence = pr;
|
||||
}
|
||||
);
|
||||
|
||||
a1.join();
|
||||
a2.join();
|
||||
|
||||
translator->get( friendRobloxUids, friendGamertags );
|
||||
UserIDTranslator::waitForUIDs(friendRobloxUids); // we've got nothing better to do
|
||||
}
|
||||
catch( Platform::Exception^ e )
|
||||
{
|
||||
dprintf( "%s exception [0x%x] '%S'. Not an error, but the returned data might be incomplete.\n", __FUNCTION__, (int)e->HResult, e->Message->Data() );
|
||||
// The function should still generate a well-formed json response string.
|
||||
// Talk to Max if you're hitting this and the string is not well-formed.
|
||||
}
|
||||
|
||||
// filter out unresolved roblox ids
|
||||
std::map< std::string, int > rbidLookup;
|
||||
for( auto e : friendRobloxUids )
|
||||
{
|
||||
auto st = e->getState();
|
||||
if( st == UserIDTranslator::Ready )
|
||||
rbidLookup[e->gamertag] = e->robloxUid;
|
||||
}
|
||||
|
||||
// not so sure about the order, so...
|
||||
std::map< std::wstring, std::wstringstream > combined; // xuid -> user info
|
||||
|
||||
|
||||
for ( auto xuid : friendXuids )
|
||||
combined[xuid->Data()] << L" {\n \"xuid\" : \"" << xuid->Data() << L"\", ";
|
||||
|
||||
for ( auto profile : friendProfiles )
|
||||
{
|
||||
if ( auto str = mapget( combined, profile->XboxUserId->Data() ) )
|
||||
{
|
||||
// Yes, we repeat the gamertag data; this is a quick fix to show gamertag in app instead of display name due to us not wanting
|
||||
// to fail cert for having unsupported unicode characters.
|
||||
(*str) << L"\"gamertag\" : \"" << profile->Gamertag->Data() << L"\", " << L"\"display\" : \"" << profile->Gamertag->Data() << L"\", ";
|
||||
(*str) << L"\"robloxuid\" : ";
|
||||
|
||||
if( int robloxuid = rbidLookup[ ws2s(profile->Gamertag->Data()) ] )
|
||||
(*str) << robloxuid <<", ";
|
||||
else
|
||||
(*str) << L"null, ";
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf("Warn: got profile for %S, didn\"t ask for.\n", profile->XboxUserId->Data() );
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto presence : friendPresence )
|
||||
{
|
||||
if ( auto str = mapget( combined, presence->XboxUserId->Data() ) )
|
||||
{
|
||||
(*str) << L"\"status\" : \"" << presence->UserState.ToString()->Data() << L"\", ";
|
||||
(*str) << L"\"rich\":\n [\n";
|
||||
|
||||
comma = false;
|
||||
for ( auto r: presence->PresenceTitleRecords )
|
||||
{
|
||||
if( comma ) (*str) << ",\n";
|
||||
(*str) << L" { \"timestamp\" : \"" << r->LastModifiedDate.UniversalTime.ToString()->Data() << "\", \"device\" : \"" << r->DeviceType.ToString()->Data() << "\", \"title\" : \"" << r->TitleName->Data() << "\", \"titleId\" : \"" << r->TitleId.ToString()->Data() << "\" , \"playing\" : \"" << r->IsTitleActive.ToString()->Data() << "\", \"presence\" : \"" << r->Presence->Data() << "\"}";
|
||||
comma = true;
|
||||
}
|
||||
(*str) << L"\n ]\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf("Warn: got presence for %S, didn't ask for.\n", presence->XboxUserId->Data() );
|
||||
}
|
||||
}
|
||||
|
||||
*ret = "{ \"friends\": [\n";
|
||||
comma = false;
|
||||
for ( auto& it : combined )
|
||||
{
|
||||
it.second << L" }";
|
||||
if( comma ) *ret += ",\n";
|
||||
*ret += ws2s( it.second.str().c_str() );
|
||||
comma = true;
|
||||
}
|
||||
*ret += "\n]\n }\n";
|
||||
|
||||
return 0;
|
||||
|
||||
} catch( Platform::Exception^ e )
|
||||
{
|
||||
dprintf( "Friends exception: 0x%x %S\n", e->HResult, e->Message->Data() );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getParty(std::vector<std::string>* result, bool filter, UserIDTranslator* translator, XboxLiveContext^ xblContext )
|
||||
{
|
||||
|
||||
if(!xblContext||!xblContext->User->IsSignedIn){
|
||||
dprintf("User not signed in, friends are not available.\n");
|
||||
return 1;
|
||||
}
|
||||
XboxLiveContext^ xboxLiveContext = xblContext;
|
||||
struct PartyUserInfo
|
||||
{
|
||||
Platform::String^ xuid;
|
||||
Platform::String^ gamertag;
|
||||
int robloxuid;
|
||||
int online;
|
||||
};
|
||||
|
||||
std::map< std::string, PartyUserInfo > partyMembers; // xuid->party member info
|
||||
std::map< std::string, PartyUserInfo* > gtlookup; // gamertag->same party member info
|
||||
std::vector< Platform::String^ > partyMemberXuids; // list of xbox party members
|
||||
std::vector< std::string > allgamertags; // list of xbox party members
|
||||
result->clear();
|
||||
|
||||
try
|
||||
{
|
||||
// get Xuids of party members
|
||||
async( PartyChat::GetPartyChatViewAsync() ).complete(
|
||||
[&]( IPartyChatView^ pv ) -> void
|
||||
{
|
||||
if( !pv || !pv->Members )
|
||||
return;
|
||||
for( auto mem : pv->Members )
|
||||
{
|
||||
std::string xuid = ws2s(mem->XboxUserId->Data());
|
||||
partyMemberXuids.push_back(mem->XboxUserId);
|
||||
|
||||
PartyUserInfo info = {};
|
||||
info.xuid = mem->XboxUserId;
|
||||
bool b = partyMembers.insert( std::make_pair(xuid, info) ).second;
|
||||
if( !b )
|
||||
{
|
||||
dprintf("getParty(): got a duplicate xuid [%s] from XBL.\n", xuid.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
).except(
|
||||
[=](Platform::Exception^ e)
|
||||
{
|
||||
dprintf("getParty(): exception 0x%x %S\n", e->HResult, e->Message->Data() );
|
||||
}
|
||||
).join();
|
||||
|
||||
if( partyMembers.empty() )
|
||||
{
|
||||
dprintf("NOTE: getParty(): no party members.\n");
|
||||
return 1; // no party
|
||||
}
|
||||
|
||||
// look up user profiles to get their gamertags
|
||||
async( xboxLiveContext->ProfileService->GetUserProfilesAsync( ref new Platform::Collections::VectorView< Platform::String^ > ( partyMemberXuids ) ) ).complete(
|
||||
[&]( IVectorView< XboxUserProfile^ > ^ profiles ) -> void
|
||||
{
|
||||
for( auto pr : profiles )
|
||||
{
|
||||
std::string xuid = ws2s( pr->XboxUserId->Data() );
|
||||
if( PartyUserInfo* info = mapget(partyMembers, xuid) )
|
||||
{
|
||||
std::string gamertag = ws2s( pr->Gamertag->Data() );
|
||||
allgamertags.push_back( gamertag );
|
||||
info->gamertag = pr->Gamertag;
|
||||
gtlookup[gamertag] = info;
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf("getParty(): got gamertag for [%s], didn't ask for.\n", xuid.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
).join();
|
||||
|
||||
if( !filter )
|
||||
{
|
||||
result->swap(allgamertags);
|
||||
return 0; // done
|
||||
}
|
||||
|
||||
// check that these players are online and have roblox ids
|
||||
std::vector< const UserIDTranslator::Entry* > robloxuids;
|
||||
translator->get( robloxuids, allgamertags );
|
||||
|
||||
// in parallel, fetch friends' online status
|
||||
async( xboxLiveContext->PresenceService->GetPresenceForMultipleUsersAsync( ref new Platform::Collections::VectorView< Platform::String^ >( partyMemberXuids ) ) ).complete(
|
||||
[&]( IVectorView<PresenceRecord^>^ friends )
|
||||
{
|
||||
for( auto rec : friends )
|
||||
{
|
||||
std::string xuid = ws2s(rec->XboxUserId->Data());
|
||||
if( PartyUserInfo* info = mapget(partyMembers, xuid) )
|
||||
{
|
||||
info->online = rec->UserState == UserPresenceState::Online;
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf("getParty(): got presence for [%s], didn't ask for.\n", xuid.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
).join();
|
||||
|
||||
UserIDTranslator::waitForUIDs(robloxuids);
|
||||
|
||||
// finally, fill out roblox userids
|
||||
for( int j=0, e=robloxuids.size(); j<e; ++j )
|
||||
{
|
||||
auto st = robloxuids[j]->getState();
|
||||
if( st == UserIDTranslator::Ready )
|
||||
{
|
||||
if( PartyUserInfo* info = gtlookup[ robloxuids[j]->gamertag ] )
|
||||
{
|
||||
info->robloxuid = robloxuids[j]->robloxUid;
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf("getParty(): got Roblox UserId for [%s], didn't ask for.\n", robloxuids[j]->gamertag.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now fill out the result
|
||||
for( auto& it : partyMembers )
|
||||
{
|
||||
auto& info = it.second;
|
||||
if( info.online && info.robloxuid > 0 )
|
||||
{
|
||||
result->push_back(ws2s(info.gamertag->Data()));
|
||||
}
|
||||
}
|
||||
|
||||
if( result->empty() )
|
||||
{
|
||||
dprintf("NOTE: getParty(): all players were filtered out.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch( Platform::Exception^ e )
|
||||
{
|
||||
dprintf( "ERROR: getParty() exception: 0x%x %s\n", e->HResult, e->Message->Data() );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Xbox live events (used for achievements)
|
||||
|
||||
void xboxEvents_init()
|
||||
{
|
||||
EventRegisterRBLX_1465F7BC();
|
||||
}
|
||||
|
||||
void xboxEvents_shutdown()
|
||||
{
|
||||
EventUnregisterRBLX_1465F7BC();
|
||||
}
|
||||
|
||||
static ULONG sendEvent( const ETX_EVENT_DESCRIPTOR* desc, const wchar_t* userId, const GUID* playerSessionId )
|
||||
{
|
||||
enum { kNArgs = 3 };
|
||||
EVENT_DATA_DESCRIPTOR EventData[kNArgs];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (userId != NULL) ? userId : L"", (userId != NULL) ? (ULONG)((wcslen(userId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], playerSessionId, sizeof(GUID));
|
||||
|
||||
try{
|
||||
return EtxEventWrite(desc, &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, kNArgs, EventData);
|
||||
}catch(Platform::Exception^ e){
|
||||
dprintf( "%s exception [0x%x]: %S\n", __FUNCTION__, (int)e->HResult, e->Message->Data() );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static ULONG sendEvent( const ETX_EVENT_DESCRIPTOR* desc, const wchar_t* userId, const GUID* playerSessionId, double value)
|
||||
{
|
||||
enum { kNArgs = 4 };
|
||||
EVENT_DATA_DESCRIPTOR EventData[kNArgs];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (userId != NULL) ? userId : L"", (userId != NULL) ? (ULONG)((wcslen(userId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], playerSessionId, sizeof(GUID));
|
||||
|
||||
float valFlt = 0;
|
||||
int valInt = 0;
|
||||
|
||||
switch (desc->FieldDescriptors[3].Type)
|
||||
{
|
||||
case EtxFieldType_Int32:
|
||||
{
|
||||
valInt = (int)value;
|
||||
EventDataDescCreate(&EventData[3], &valInt, sizeof(int));
|
||||
break;
|
||||
}
|
||||
case EtxFieldType_Float:
|
||||
{
|
||||
valFlt = (float)value;
|
||||
EventDataDescCreate(&EventData[3], &valFlt, sizeof(float));
|
||||
break;
|
||||
}
|
||||
case EtxFieldType_Double:
|
||||
{
|
||||
EventDataDescCreate(&EventData[3], &value, sizeof(double));
|
||||
break;
|
||||
}
|
||||
default: RBXASSERT(false); // unsupported type
|
||||
}
|
||||
|
||||
|
||||
try{
|
||||
return EtxEventWrite(desc, &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, kNArgs, EventData);
|
||||
}catch(Platform::Exception^ e){
|
||||
dprintf( "%s exception [0x%x]: %S\n", __FUNCTION__, (int)e->HResult, e->Message->Data() );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static const ETX_EVENT_DESCRIPTOR* findEvent( const char* name )
|
||||
{
|
||||
const ETX_EVENT_DESCRIPTOR* const table = RBLX_1465F7BCEvents;
|
||||
enum { kCount = sizeof(RBLX_1465F7BCEvents) / sizeof(RBLX_1465F7BCEvents[0]) };
|
||||
|
||||
for( int j=0; j<kCount; ++j )
|
||||
{
|
||||
if( strcmp( table[j].Name, name ) == 0 )
|
||||
return &table[j];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
RBX::AwardResult xboxEvents_send( Microsoft::Xbox::Services::XboxLiveContext^ xboxLiveContext, const char* evtName, double *value /*= NULL*/)
|
||||
{
|
||||
if( !xboxLiveContext ) return RBX::Award_NoUser;
|
||||
|
||||
auto e = findEvent(evtName);
|
||||
if( !e ) return RBX::Award_NotFound;
|
||||
|
||||
ULONG r = 1;
|
||||
if (!value)
|
||||
r = sendEvent( e, xboxLiveContext->User->XboxUserId->Data(), &IID_IUnknown);
|
||||
else
|
||||
r = sendEvent( e, xboxLiveContext->User->XboxUserId->Data(), &IID_IUnknown, *value);
|
||||
|
||||
return r == 0 ? RBX::Award_OK : RBX::Award_Fail;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// XboxLive stuff
|
||||
//
|
||||
// Very toxic, do not include in headers!
|
||||
//
|
||||
|
||||
#include <collection.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "v8datamodel/PlatformService.h"
|
||||
|
||||
class UserIDTranslator;
|
||||
class XboxPlatform;
|
||||
|
||||
using namespace Microsoft::Xbox::Services;
|
||||
using namespace Microsoft::Xbox::Services::Multiplayer;
|
||||
|
||||
int getFriends(std::string* ret, UserIDTranslator* translator, Microsoft::Xbox::Services::XboxLiveContext^ xboxLiveContext );
|
||||
int getParty(std::vector<std::string>* result, bool filter, UserIDTranslator* translator, Microsoft::Xbox::Services::XboxLiveContext^ xboxLiveContext);
|
||||
|
||||
void xboxEvents_init();
|
||||
void xboxEvents_shutdown();
|
||||
RBX::AwardResult xboxEvents_send( Microsoft::Xbox::Services::XboxLiveContext^ xboxLiveContext, const char* evtName, double* value = NULL ); // reentrant
|
||||
@@ -0,0 +1,441 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Durango">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Durango</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="NoOpt|ARM">
|
||||
<Configuration>NoOpt</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="NoOpt|Durango">
|
||||
<Configuration>NoOpt</Configuration>
|
||||
<Platform>Durango</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Durango">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Durango</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A321A726-13A3-4884-ADDA-B0DCCB600765}</ProjectGuid>
|
||||
<RootNamespace>XboxClient</RootNamespace>
|
||||
<ApplicationEnvironment>title</ApplicationEnvironment>
|
||||
<SccProjectName>Perforce Project</SccProjectName>
|
||||
<SccLocalPath>.</SccLocalPath>
|
||||
<SccProvider>MSSCCI:Perforce SCM</SccProvider>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='NoOpt|Durango'">
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoOpt|ARM'" Label="Configuration">
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
<OutDir>bin\$(Configuration)$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration)$(Platform)\</IntDir>
|
||||
<ExecutablePath>$(Console_SdkRoot)bin;$(Console_SdkRoot)xdk\fxc\amd64;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
|
||||
<IncludePath>$(Console_SdkIncludeRoot)\um;$(Console_SdkIncludeRoot)\shared;$(Console_SdkIncludeRoot)\winrt</IncludePath>
|
||||
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
|
||||
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
|
||||
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
<OutDir>bin\$(Configuration)$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration)$(Platform)\</IntDir>
|
||||
<ExecutablePath>$(Console_SdkRoot)bin;$(Console_SdkRoot)xdk\fxc\amd64;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
|
||||
<IncludePath>$(Console_SdkIncludeRoot)\um;$(Console_SdkIncludeRoot)\shared;$(Console_SdkIncludeRoot)\winrt</IncludePath>
|
||||
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
|
||||
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
|
||||
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoOpt|Durango'">
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
<OutDir>bin\$(Configuration)$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration)$(Platform)\</IntDir>
|
||||
<ExecutablePath>$(Console_SdkRoot)bin;$(Console_SdkRoot)xdk\fxc\amd64;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH)</ExecutablePath>
|
||||
<IncludePath>$(Console_SdkIncludeRoot)\um;$(Console_SdkIncludeRoot)\shared;$(Console_SdkIncludeRoot)\winrt</IncludePath>
|
||||
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
|
||||
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
|
||||
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoOpt|ARM'">
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
<OutDir>bin\$(Configuration)$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration)$(Platform)\</IntDir>
|
||||
<ExecutablePath>$(Console_SdkRoot)bin;$(Console_SdkRoot)xdk\fxc\amd64;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH)</ExecutablePath>
|
||||
<IncludePath>$(Console_SdkIncludeRoot)\um;$(Console_SdkIncludeRoot)\shared;$(Console_SdkIncludeRoot)\winrt</IncludePath>
|
||||
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
|
||||
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
|
||||
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
<OutDir>bin\$(Configuration)$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration)$(Platform)\</IntDir>
|
||||
<ExecutablePath>$(Console_SdkRoot)bin;$(Console_SdkRoot)xdk\fxc\amd64;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
|
||||
<IncludePath>$(Console_SdkIncludeRoot)\um;$(Console_SdkIncludeRoot)\shared;$(Console_SdkIncludeRoot)\winrt</IncludePath>
|
||||
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
|
||||
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
|
||||
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
|
||||
<OutDir>bin\$(Configuration)$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration)$(Platform)\</IntDir>
|
||||
<ExecutablePath>$(Console_SdkRoot)bin;$(Console_SdkRoot)xdk\fxc\amd64;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
|
||||
<IncludePath>$(Console_SdkIncludeRoot)\um;$(Console_SdkIncludeRoot)\shared;$(Console_SdkIncludeRoot)\winrt</IncludePath>
|
||||
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
|
||||
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
|
||||
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='NoOpt|Durango'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>$(CONTRIB_PATH)\SDL2\Include;..\ClientBase;$(CONTRIB_PATH)\boost_1_56_0\include;..\Win;..\App\include;..\ClientShared\;..\Base\include;..\Network\include;..\Log\include;..\Rendering\GfxCore\include;..\Rendering\GfxBase\include;..\Rendering\g3d\include;..\Rendering\RbxG3D\include;$(CONTRIB_PATH)\DSBaseClasses\Sources;$(CONTRIB_PATH)\VMProtectWin_2.13;..\fmod\xbox\include;.;..\App.BulletPhysics;$(CONTRIB_PATH)\SDL\SDL2-2.0.3\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>RBX_PLATFORM_DURANGO;_TITLE;MONOLITHIC=1;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;_NOOPT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<OmitFramePointers>false</OmitFramePointers>
|
||||
<ForcedUsingFiles>"C:\Program Files (x86)\Microsoft Durango XDK\xdk\Extensions\Xbox Services API\8.0\References\CommonConfiguration\neutral\Microsoft.Xbox.Services.winmd";%(ForcedUsingFiles)</ForcedUsingFiles>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>etwplus.lib;mmdevapi.lib;d3d11_x.lib;xg_x.lib;combase.lib;toolhelpx.lib;D3DCompiler.lib;dxguid.lib;ws2_32.lib;kernelx.lib;zlib.lib;fmod_vc.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreSpecificDefaultLibraries>tbb.lib;psapi.lib;version.lib;kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalLibraryDirectories>..\zlib\win\bin\ReleaseDurango;..\fmod\xbox\lib</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>python fetchfflags.py > $(LayoutDir)Image\Loose\fflags.json
|
||||
xcopy /b /y $(ProjectDir)img\* $(LayoutDir)Image\Loose\img\
|
||||
xcopy /b /y $(SolutionDir)fmod\xbox\fmodex.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Debug\Microsoft.Xbox.Samples.NetworkMesh.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Debug\Microsoft.Xbox.Samples.NetworkMesh.pdb "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Debug\Microsoft.Xbox.GameChat.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Debug\Microsoft.Xbox.GameChat.pdb "$(LayoutDir)Image\Loose"
|
||||
call "$(SolutionDir)buildshaders.bat"
|
||||
</Command>
|
||||
</PostBuildEvent>
|
||||
<PreBuildEvent>
|
||||
<Command>..\CoreScriptConverter2\tool\win32\BuildCoreScripts.bat ..\xboxrsc.config</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='NoOpt|ARM'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>$(CONTRIB_PATH)\SDL2\Include;..\ClientBase;$(CONTRIB_PATH)\boost_1_56_0\include;..\Win;..\App\include;..\ClientShared\;..\Base\include;..\Network\include;..\Log\include;..\Rendering\GfxCore\include;..\Rendering\GfxBase\include;..\Rendering\g3d\include;..\Rendering\RbxG3D\include;$(CONTRIB_PATH)\DSBaseClasses\Sources;$(CONTRIB_PATH)\VMProtectWin_2.13;..\fmod\xbox\include;.;..\App.BulletPhysics;$(CONTRIB_PATH)\SDL\SDL2-2.0.3\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>RBX_PLATFORM_DURANGO;_TITLE;MONOLITHIC=1;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;_NOOPT;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<OmitFramePointers>false</OmitFramePointers>
|
||||
<ForcedUsingFiles>"C:\Program Files (x86)\Microsoft Durango XDK\xdk\Extensions\Xbox Services API\8.0\References\CommonConfiguration\neutral\Microsoft.Xbox.Services.winmd";%(ForcedUsingFiles)</ForcedUsingFiles>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>etwplus.lib;mmdevapi.lib;d3d11_x.lib;xg_x.lib;combase.lib;toolhelpx.lib;D3DCompiler.lib;dxguid.lib;ws2_32.lib;kernelx.lib;zlib.lib;fmod_vc.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreSpecificDefaultLibraries>tbb.lib;psapi.lib;version.lib;kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalLibraryDirectories>..\zlib\win\bin\ReleaseDurango;..\fmod\xbox\lib</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>python fetchfflags.py > $(LayoutDir)Image\Loose\fflags.json
|
||||
xcopy /b /y $(ProjectDir)img\* $(LayoutDir)Image\Loose\img\
|
||||
xcopy /b /y $(SolutionDir)fmod\xbox\fmodex.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Debug\Microsoft.Xbox.Samples.NetworkMesh.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Debug\Microsoft.Xbox.Samples.NetworkMesh.pdb "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Debug\Microsoft.Xbox.GameChat.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Debug\Microsoft.Xbox.GameChat.pdb "$(LayoutDir)Image\Loose"
|
||||
call "$(SolutionDir)buildshaders.bat"
|
||||
</Command>
|
||||
</PostBuildEvent>
|
||||
<PreBuildEvent>
|
||||
<Command>..\CoreScriptConverter2\tool\win32\BuildCoreScripts.bat ..\xboxrsc.config</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>RBX_PLATFORM_DURANGO;_TITLE;MONOLITHIC=1;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(CONTRIB_PATH)\SDL2\Include;..\ClientBase;$(CONTRIB_PATH)\boost_1_56_0\include;..\Win;..\App\include;..\Base\include;..\Network\include;..\Log\include;..\Rendering\GfxCore\include;..\Rendering\GfxBase\include;..\Rendering\g3d\include;..\Rendering\RbxG3D\include;$(CONTRIB_PATH)\DSBaseClasses\Sources;$(CONTRIB_PATH)\VMProtectWin_2.13;..\fmod\xbox\include;.;..\App.BulletPhysics;$(CONTRIB_PATH)\SDL\SDL2-2.0.3\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>etwplus.lib;mmdevapi.lib;d3d11_x.lib;xg_x.lib;combase.lib;toolhelpx.lib;D3DCompiler.lib;dxguid.lib;etwplus.lib;ws2_32.lib;kernelx.lib;zlib.lib;fmodex_static.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<Link>
|
||||
<IgnoreSpecificDefaultLibraries>tbb.lib;psapi.lib;version.lib;kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib</IgnoreSpecificDefaultLibraries>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalLibraryDirectories>..\zlib\win\bin\ReleaseDurango;..\fmod\xbox</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>python fetchfflags.py > $(LayoutDir)Image\Loose\fflags.json
|
||||
xcopy /b /y $(ProjectDir)img\* $(LayoutDir)Image\Loose\img\
|
||||
call "$(SolutionDir)buildshaders.bat"
|
||||
"$(DurangoXDK)bin\xbcp" NSAL.json XS:\NSAL.json
|
||||
"$(DurangoXDK)bin\xbcp" enforceNSAL XS:\enforceNSAL</Command>
|
||||
</PostBuildEvent>
|
||||
<PreBuildEvent>
|
||||
<Command>..\CoreScriptConverter2\tool\win32\BuildCoreScripts.bat ..\xboxrsc.config</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>RBX_PLATFORM_DURANGO;_TITLE;MONOLITHIC=1;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(CONTRIB_PATH)\SDL2\Include;..\ClientBase;$(CONTRIB_PATH)\boost_1_56_0\include;..\Win;..\App\include;..\Base\include;..\Network\include;..\Log\include;..\Rendering\GfxCore\include;..\Rendering\GfxBase\include;..\Rendering\g3d\include;..\Rendering\RbxG3D\include;$(CONTRIB_PATH)\DSBaseClasses\Sources;$(CONTRIB_PATH)\VMProtectWin_2.13;..\fmod\xbox\include;.;..\App.BulletPhysics;$(CONTRIB_PATH)\SDL\SDL2-2.0.3\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>etwplus.lib;mmdevapi.lib;d3d11_x.lib;xg_x.lib;combase.lib;toolhelpx.lib;D3DCompiler.lib;dxguid.lib;etwplus.lib;ws2_32.lib;kernelx.lib;zlib.lib;fmodex_static.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<Link>
|
||||
<IgnoreSpecificDefaultLibraries>tbb.lib;psapi.lib;version.lib;kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib</IgnoreSpecificDefaultLibraries>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalLibraryDirectories>..\zlib\win\bin\ReleaseDurango;..\fmod\xbox</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>python fetchfflags.py > $(LayoutDir)Image\Loose\fflags.json
|
||||
xcopy /b /y $(ProjectDir)img\* $(LayoutDir)Image\Loose\img\
|
||||
call "$(SolutionDir)buildshaders.bat"
|
||||
"$(DurangoXDK)bin\xbcp" NSAL.json XS:\NSAL.json
|
||||
"$(DurangoXDK)bin\xbcp" enforceNSAL XS:\enforceNSAL</Command>
|
||||
</PostBuildEvent>
|
||||
<PreBuildEvent>
|
||||
<Command>..\CoreScriptConverter2\tool\win32\BuildCoreScripts.bat ..\xboxrsc.config</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">
|
||||
<Link>
|
||||
<AdditionalDependencies>etwplus.lib;mmdevapi.lib;d3d11_x.lib;xg_x.lib;combase.lib;toolhelpx.lib;D3DCompiler.lib;dxguid.lib;ws2_32.lib;kernelx.lib;zlib.lib;fmod_vc.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Link>
|
||||
<AdditionalDependencies>etwplus.lib;mmdevapi.lib;d3d11_x.lib;xg_x.lib;combase.lib;toolhelpx.lib;D3DCompiler.lib;dxguid.lib;ws2_32.lib;kernelx.lib;zlib.lib;fmod_vc.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">
|
||||
<Link>
|
||||
<IgnoreSpecificDefaultLibraries>tbb.lib;psapi.lib;version.lib;kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib</IgnoreSpecificDefaultLibraries>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalLibraryDirectories>..\zlib\win\bin\ReleaseDurango;..\fmod\xbox\lib</AdditionalLibraryDirectories>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>python fetchfflags.py > $(LayoutDir)Image\Loose\fflags.json
|
||||
xcopy /b /y $(ProjectDir)img\* $(LayoutDir)Image\Loose\img\
|
||||
xcopy /b /y $(SolutionDir)fmod\xbox\fmodex.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Release\Microsoft.Xbox.Samples.NetworkMesh.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Release\Microsoft.Xbox.Samples.NetworkMesh.pdb "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Release\Microsoft.Xbox.GameChat.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Release\Microsoft.Xbox.GameChat.pdb "$(LayoutDir)Image\Loose"
|
||||
call "$(SolutionDir)buildshaders.bat"
|
||||
</Command>
|
||||
</PostBuildEvent>
|
||||
<ClCompile>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>$(CONTRIB_PATH)\SDL2\Include;..\ClientBase;$(CONTRIB_PATH)\boost_1_56_0\include;..\Win;..\App\include;..\ClientShared\;..\Base\include;..\Network\include;..\Log\include;..\Rendering\GfxCore\include;..\Rendering\GfxBase\include;..\Rendering\g3d\include;..\Rendering\RbxG3D\include;$(CONTRIB_PATH)\DSBaseClasses\Sources;$(CONTRIB_PATH)\VMProtectWin_2.13;..\fmod\xbox\include;.;..\App.BulletPhysics;$(CONTRIB_PATH)\SDL\SDL2-2.0.3\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>RBX_PLATFORM_DURANGO;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalUsingDirectories>$(Console_SdkWindowsMetadataPath)</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<PreBuildEvent>
|
||||
<Command>..\CoreScriptConverter2\tool\win32\BuildCoreScripts.bat ..\xboxrsc.config</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Link>
|
||||
<IgnoreSpecificDefaultLibraries>tbb.lib;psapi.lib;version.lib;kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib</IgnoreSpecificDefaultLibraries>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalLibraryDirectories>..\zlib\win\bin\ReleaseDurango;..\fmod\xbox\lib</AdditionalLibraryDirectories>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>python fetchfflags.py > $(LayoutDir)Image\Loose\fflags.json
|
||||
xcopy /b /y $(ProjectDir)img\* $(LayoutDir)Image\Loose\img\
|
||||
xcopy /b /y $(SolutionDir)fmod\xbox\fmodex.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Release\Microsoft.Xbox.Samples.NetworkMesh.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)Microsoft.Xbox.Samples.NetworkMesh\Durango\Release\Microsoft.Xbox.Samples.NetworkMesh.pdb "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Release\Microsoft.Xbox.GameChat.dll "$(LayoutDir)Image\Loose"
|
||||
xcopy /b /y $(SolutionDir)GameChat\Durango\Release\Microsoft.Xbox.GameChat.pdb "$(LayoutDir)Image\Loose"
|
||||
call "$(SolutionDir)buildshaders.bat"
|
||||
</Command>
|
||||
</PostBuildEvent>
|
||||
<ClCompile>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>$(CONTRIB_PATH)\SDL2\Include;..\ClientBase;$(CONTRIB_PATH)\boost_1_56_0\include;..\Win;..\App\include;..\ClientShared\;..\Base\include;..\Network\include;..\Log\include;..\Rendering\GfxCore\include;..\Rendering\GfxBase\include;..\Rendering\g3d\include;..\Rendering\RbxG3D\include;$(CONTRIB_PATH)\DSBaseClasses\Sources;$(CONTRIB_PATH)\VMProtectWin_2.13;..\fmod\xbox\include;.;..\App.BulletPhysics;$(CONTRIB_PATH)\SDL\SDL2-2.0.3\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>RBX_PLATFORM_DURANGO;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalUsingDirectories>$(Console_SdkWindowsMetadataPath)</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<PreBuildEvent>
|
||||
<Command>..\CoreScriptConverter2\tool\win32\BuildCoreScripts.bat ..\xboxrsc.config</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\App\script\LuaVMClient.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoOpt|Durango'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoOpt|ARM'">false</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\App\script\LuaVMDummy.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoOpt|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoOpt|ARM'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\ClientBase\RenderSettingsItem.cpp" />
|
||||
<ClCompile Include="..\ClientShared\DataModelEmptySerialize.cpp" />
|
||||
<ClCompile Include="async.cpp" />
|
||||
<ClCompile Include="ControllerBuffer.cpp" />
|
||||
<ClCompile Include="fmodstub.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoOpt|Durango'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoOpt|ARM'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyboardProvider.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="marshaller.cpp" />
|
||||
<ClCompile Include="p2p.cpp" />
|
||||
<ClCompile Include="renderJob.cpp" />
|
||||
<ClCompile Include="UserTranslator.cpp" />
|
||||
<ClCompile Include="VoiceChat.cpp" />
|
||||
<ClCompile Include="VoiceChatMaxNet.cpp" />
|
||||
<ClCompile Include="XbLiveUtils.cpp" />
|
||||
<ClCompile Include="XboxGameController.cpp" />
|
||||
<ClCompile Include="XboxMultiplayerManager.cpp" />
|
||||
<ClCompile Include="XboxService.cpp" />
|
||||
<ClCompile Include="XboxUtils.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\App.BulletPhysics\App.BulletPhysics.vcxproj">
|
||||
<Project>{c9285114-dcc1-4e90-aa76-f590cefb94c2}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\App\App.vcxproj">
|
||||
<Project>{63db8347-940b-4a05-8975-6a6545c315dc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Base\Base.vcxproj">
|
||||
<Project>{3025c00a-7746-469b-a279-96127c72abee}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\boostlibs\boost.static.vcxproj">
|
||||
<Project>{5423bfb6-d3eb-4b00-a82b-38001eb8745f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\CSG\CSG.vcxproj">
|
||||
<Project>{9b6c66f7-6887-44d3-ac5e-3c2bd94661fc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GameChat\Microsoft.Xbox.GameChat.XDK.vcxproj">
|
||||
<Project>{9b399639-7a3f-44cf-82ef-d4c50718130e}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Log\Log.vcxproj">
|
||||
<Project>{9980f6c3-b64d-4775-8793-bb6dde31ce82}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Microsoft.Xbox.Samples.NetworkMesh\Microsoft.Xbox.Samples.NetworkMesh.vcxproj">
|
||||
<Project>{9b399639-7a3f-44cf-82ef-d4c50718120e}</Project>
|
||||
<Private>true</Private>
|
||||
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
<LinkLibraryDependencies>true</LinkLibraryDependencies>
|
||||
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Network\Network.vcxproj">
|
||||
<Project>{9b9eb5c6-15d1-4765-bcc7-8a42f2c9f6cc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Rendering\AppDraw\AppDraw.vcxproj">
|
||||
<Project>{43afcf25-5133-4978-8b2c-d02ee0eee199}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Rendering\g3d\graphics3D.vcxproj">
|
||||
<Project>{05c08695-1eca-489c-815d-d74d65f353f0}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Rendering\GfxBase\GfxBase.vcxproj">
|
||||
<Project>{857de167-1ed8-4e4d-955a-5cc5cc3944c1}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Rendering\GfxCore\GfxCore.vcxproj">
|
||||
<Project>{3a10c3ec-1b27-427e-b955-87ee6b176130}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Rendering\GfxRender\GfxRender.vcxproj">
|
||||
<Project>{9b7cce5d-2877-4321-8910-3c8e3936f62f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Rendering\RbxG3D\RbxG3D.vcxproj">
|
||||
<Project>{7d55bdab-c90b-4b36-9b2c-af8ef3e9129f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\zlib\win\zlib.vcxproj">
|
||||
<Project>{85ff2e31-e863-40cc-b1d6-c6c1875ec5a1}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\ClientBase\RenderSettingsItem.h" />
|
||||
<ClInclude Include="async.h" />
|
||||
<ClInclude Include="ControllerBuffer.h" />
|
||||
<ClInclude Include="KeyboardProvider.h" />
|
||||
<ClInclude Include="marshaller.h" />
|
||||
<ClInclude Include="p2p.h" />
|
||||
<ClInclude Include="renderJob.h" />
|
||||
<ClInclude Include="UserTranslator.h" />
|
||||
<ClInclude Include="VoiceChat.h" />
|
||||
<ClInclude Include="VoiceChatBase.h" />
|
||||
<ClInclude Include="VoiceChatMaxNet.h" />
|
||||
<ClInclude Include="XbLiveUtils.h" />
|
||||
<ClInclude Include="XboxGameController.h" />
|
||||
<ClInclude Include="XboxMultiplayerManager.h" />
|
||||
<ClInclude Include="XboxService.h" />
|
||||
<ClInclude Include="XboxUtils.h" />
|
||||
<ClInclude Include="xdpevents.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\fmod\xbox\fmod.dll">
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='NoOpt|Durango'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='NoOpt|ARM'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">true</DeploymentContent>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent>
|
||||
</None>
|
||||
<None Include="NSAL.json" />
|
||||
<None Include="xdpevents.bat" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<SDKReference Include="Xbox Services API, Version=8.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="xdpevents.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="..\ClientBase\RenderSettingsItem.cpp">
|
||||
<Filter>ClientBase</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\ClientShared\DataModelEmptySerialize.cpp">
|
||||
<Filter>ClientShared</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="XboxGameController.cpp" />
|
||||
<ClCompile Include="..\App\script\LuaVMClient.cpp" />
|
||||
<ClCompile Include="renderJob.cpp" />
|
||||
<ClCompile Include="marshaller.cpp" />
|
||||
<ClCompile Include="XboxService.cpp" />
|
||||
<ClCompile Include="XboxUtils.cpp" />
|
||||
<ClCompile Include="async.cpp" />
|
||||
<ClCompile Include="ControllerBuffer.cpp" />
|
||||
<ClCompile Include="UserTranslator.cpp" />
|
||||
<ClCompile Include="XbLiveUtils.cpp" />
|
||||
<ClCompile Include="KeyboardProvider.cpp" />
|
||||
<ClCompile Include="VoiceChat.cpp" />
|
||||
<ClCompile Include="XboxMultiplayerManager.cpp" />
|
||||
<ClCompile Include="p2p.cpp" />
|
||||
<ClCompile Include="..\App\script\LuaVMDummy.cpp" />
|
||||
<ClCompile Include="fmodstub.cpp" />
|
||||
<ClCompile Include="VoiceChatMaxNet.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="ClientBase">
|
||||
<UniqueIdentifier>{91536e1d-2944-4a65-89bd-86ea5d84942f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ClientShared">
|
||||
<UniqueIdentifier>{c20ed3de-040a-4e8b-94de-11a3b24f3ab5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Libraries">
|
||||
<UniqueIdentifier>{db898056-59d7-4be5-9299-25f948c6d53e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\ClientBase\RenderSettingsItem.h">
|
||||
<Filter>ClientBase</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="XboxGameController.h" />
|
||||
<ClInclude Include="marshaller.h" />
|
||||
<ClInclude Include="renderJob.h" />
|
||||
<ClInclude Include="XboxService.h" />
|
||||
<ClInclude Include="XboxUtils.h" />
|
||||
<ClInclude Include="async.h" />
|
||||
<ClInclude Include="ControllerBuffer.h" />
|
||||
<ClInclude Include="UserTranslator.h" />
|
||||
<ClInclude Include="XbLiveUtils.h" />
|
||||
<ClInclude Include="KeyboardProvider.h" />
|
||||
<ClInclude Include="xdpevents.h" />
|
||||
<ClInclude Include="XboxMultiplayerManager.h" />
|
||||
<ClInclude Include="VoiceChat.h" />
|
||||
<ClInclude Include="p2p.h" />
|
||||
<ClInclude Include="VoiceChatMaxNet.h" />
|
||||
<ClInclude Include="VoiceChatBase.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="NSAL.json" />
|
||||
<None Include="xdpevents.bat" />
|
||||
<None Include="..\fmod\xbox\fmod.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="xdpevents.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,411 @@
|
||||
#include "XboxGameController.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "reflection/type.h"
|
||||
#include "v8datamodel/DataModel.h"
|
||||
#include "v8datamodel/UserInputService.h"
|
||||
#include "v8datamodel/GamepadService.h"
|
||||
|
||||
using namespace boost;
|
||||
using namespace RBX;
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
XboxGameController::XboxGameController(shared_ptr<RBX::DataModel> newDM, shared_ptr<ControllerBuffer> newControllerBuffer) :
|
||||
controllerBuffer(newControllerBuffer)
|
||||
{
|
||||
currentVibration.LeftMotorLevel = 0;
|
||||
currentVibration.RightMotorLevel = 0;
|
||||
currentVibration.LeftTriggerLevel = 0;
|
||||
currentVibration.RightTriggerLevel = 0;
|
||||
|
||||
controllerBuffer->setXboxGameController(this);
|
||||
initializedControllerGuidMap = false;
|
||||
|
||||
setDataModel(newDM);
|
||||
}
|
||||
|
||||
void XboxGameController::handleNewGamepad(IGamepad^ gamepad)
|
||||
{
|
||||
int rbxGamepadInt = getRbxGamepadIntFromGamepad(gamepad);
|
||||
|
||||
if (rbxGamepadInt >= 0)
|
||||
{
|
||||
gamepadConnectedMap[rbxGamepadInt].isConnected = true;
|
||||
gamepadConnectedMap[rbxGamepadInt].guid = gamepad->Id;
|
||||
|
||||
fireGamepadConnectedEvent(rbxGamepadInt);
|
||||
}
|
||||
}
|
||||
|
||||
void XboxGameController::handleGamepadRemove(IGamepad^ gamepad)
|
||||
{
|
||||
gamepadConnectedMap.erase(gamepad->Id);
|
||||
|
||||
fireGamepadDisconnectedEvent(gamepad->Id);
|
||||
}
|
||||
|
||||
void XboxGameController::setupGamepadHandling()
|
||||
{
|
||||
// first clear out all old data
|
||||
Windows::Xbox::Input::Gamepad::GamepadAdded -= gamepadAddedEvent;
|
||||
Windows::Xbox::Input::Gamepad::GamepadRemoved -= gamepadRemovedEvent;
|
||||
|
||||
gamepadConnectedMap.clear();
|
||||
|
||||
// now lets setup all existing gamepads
|
||||
IVectorView<IGamepad^>^ gamepads = Windows::Xbox::Input::Gamepad::Gamepads;
|
||||
for ( unsigned int i = 0; i < gamepads->Size; ++i )
|
||||
{
|
||||
handleNewGamepad(gamepads->GetAt(i));
|
||||
}
|
||||
|
||||
// finally listen to gamepads being removed and added
|
||||
gamepadAddedEvent = Windows::Xbox::Input::Gamepad::GamepadAdded += ref new EventHandler<GamepadAddedEventArgs^ >( [=]( Platform::Object^ , GamepadAddedEventArgs^ args )
|
||||
{
|
||||
handleNewGamepad(args->Gamepad);
|
||||
});
|
||||
gamepadRemovedEvent = Windows::Xbox::Input::Gamepad::GamepadRemoved += ref new EventHandler<GamepadRemovedEventArgs^ >( [=]( Platform::Object^ , GamepadRemovedEventArgs^ args )
|
||||
{
|
||||
handleGamepadRemove(args->Gamepad);
|
||||
});
|
||||
}
|
||||
|
||||
void XboxGameController::resetControllerGuidMap()
|
||||
{
|
||||
initializedControllerGuidMap = false;
|
||||
xboxLiveIdToRBXGamepadType.clear();
|
||||
}
|
||||
|
||||
void XboxGameController::setDataModel(shared_ptr<RBX::DataModel> newDM)
|
||||
{
|
||||
dataModel = newDM;
|
||||
|
||||
setupGamepadHandling();
|
||||
|
||||
if ( RBX::UserInputService* inputService = RBX::ServiceProvider::find<UserInputService>(newDM.get()) )
|
||||
{
|
||||
getSupportedGamepadKeyCodesConnection.disconnect();
|
||||
getSupportedGamepadKeyCodesConnection = inputService->getSupportedGamepadKeyCodesSignal.connect(boost::bind(&XboxGameController::findAvailableGamepadKeyCodesAndSet, this, _1));
|
||||
|
||||
updateInputConnection.disconnect();
|
||||
updateInputConnection = inputService->updateInputSignal.connect(boost::bind(&XboxGameController::processControllerBuffer, this));
|
||||
}
|
||||
|
||||
if ( RBX::HapticService* hapticService = RBX::ServiceProvider::create<HapticService>(newDM.get()) )
|
||||
{
|
||||
setEnabledVibrationMotorsConnection.disconnect();
|
||||
setVibrationMotorConnection.disconnect();
|
||||
|
||||
setEnabledVibrationMotorsConnection = hapticService->setEnabledVibrationMotorsSignal.connect(boost::bind(&XboxGameController::setVibrationMotorsEnabled, this, _1));
|
||||
setVibrationMotorConnection = hapticService->setVibrationMotorSignal.connect(boost::bind(&XboxGameController::setVibrationMotor, this, _1, _2, _3));
|
||||
}
|
||||
}
|
||||
int XboxGameController::getRbxGamepadIntFromGamepad(Windows::Xbox::Input::IGamepad^ gamepad)
|
||||
{
|
||||
if (!initializedControllerGuidMap)
|
||||
{
|
||||
IVectorView<IGamepad^>^ gamepads = Windows::Xbox::Input::Gamepad::Gamepads;
|
||||
for ( unsigned int i = 0; i < gamepads->Size; ++i )
|
||||
{
|
||||
if (gamepads->GetAt(i) == gamepad)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gamepad == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
User^ user = nullptr;
|
||||
try
|
||||
{
|
||||
user = gamepad->User;
|
||||
}
|
||||
catch (std::runtime_error)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (user == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Platform::String^ xboxUserId;
|
||||
try
|
||||
{
|
||||
xboxUserId = user->XboxUserId;
|
||||
}
|
||||
catch (std::runtime_error)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (xboxUserId == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (xboxUserId->IsEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (user->IsGuest)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
RBX::InputObject::UserInputType gamepadEnum = InputObject::TYPE_NONE;
|
||||
int rbxGamepadInt = 0;
|
||||
|
||||
if (!getRBXGamepadFromXboxLiveId(xboxUserId, gamepad->Id, gamepadEnum, rbxGamepadInt))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rbxGamepadInt;
|
||||
}
|
||||
|
||||
void XboxGameController::remapXboxLiveIdToRBXGamepadId(Platform::String^ xboxLiveId, unsigned long long controllerId, RBX::InputObject::UserInputType gamepadType)
|
||||
{
|
||||
// todo: actually do remapping instead of blowing away mapping and adding one in (need this now for xbox cert)
|
||||
xboxLiveIdToRBXGamepadType.clear();
|
||||
std::pair<Platform::String^,unsigned long long> liveIdControllerIdPair(xboxLiveId,controllerId);
|
||||
xboxLiveIdToRBXGamepadType[liveIdControllerIdPair] = gamepadType;
|
||||
initializedControllerGuidMap = true;
|
||||
}
|
||||
|
||||
bool XboxGameController::getRBXGamepadFromXboxLiveId(Platform::String^ xboxLiveId, unsigned long long controllerId, RBX::InputObject::UserInputType& gamepadEnum, int& rbxGamepadInt)
|
||||
{
|
||||
std::pair<Platform::String^,unsigned long long> liveIdControllerIdPair(xboxLiveId, controllerId);
|
||||
|
||||
std::map<std::pair<Platform::String^, unsigned long long>, RBX::InputObject::UserInputType>::iterator iter = xboxLiveIdToRBXGamepadType.find(liveIdControllerIdPair);
|
||||
if (iter == xboxLiveIdToRBXGamepadType.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
gamepadEnum = iter->second;
|
||||
rbxGamepadInt = RBX::GamepadService::getGamepadIntForEnum(gamepadEnum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XboxGameController::updateVibration()
|
||||
{
|
||||
const uint64 gamepadGuid = getGamepadGuid(RBX::InputObject::TYPE_GAMEPAD1);
|
||||
IVectorView<IGamepad^>^ gamepads = Windows::Xbox::Input::Gamepad::Gamepads;
|
||||
|
||||
for ( unsigned int i = 0; i < gamepads->Size; ++i )
|
||||
{
|
||||
IGamepad^ gamepad = gamepads->GetAt(i);
|
||||
|
||||
if (gamepad->Id == gamepadGuid)
|
||||
{
|
||||
gamepad->SetVibration(currentVibration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XboxGameController::processControllerBuffer()
|
||||
{
|
||||
shared_ptr<RBX::DataModel> sharedDM = dataModel.lock();
|
||||
if (!sharedDM)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GamepadService* gamepadService = RBX::ServiceProvider::find<GamepadService>(sharedDM.get());
|
||||
if (!gamepadService)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UserInputService* inputService = RBX::ServiceProvider::find<UserInputService>(sharedDM.get());
|
||||
if (!inputService)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ControllerBuffer::GamepadValueBufferMap bufferMap = controllerBuffer->getBufferedInput();
|
||||
for (ControllerBuffer::GamepadValueBufferMap::iterator iter = bufferMap.begin(); iter != bufferMap.end(); ++iter)
|
||||
{
|
||||
RBX::Gamepad gamepad = gamepadService->getGamepadState(GamepadService::getGamepadIntForEnum((*iter).first.first));
|
||||
RBX::KeyCode keyCode = (*iter).first.second;
|
||||
ControllerBuffer::GamepadValueStateHistoryVector historyVector = (*iter).second;
|
||||
|
||||
for (ControllerBuffer::GamepadValueStateHistoryVector::iterator historyIter = historyVector.begin(); historyIter != historyVector.end(); ++historyIter)
|
||||
{
|
||||
InputObject::UserInputState newState = (*historyIter).second;
|
||||
Vector3 newValue = (*historyIter).first;
|
||||
|
||||
G3D::Vector3 lastPos = gamepad[keyCode]->getPosition();
|
||||
|
||||
if (lastPos != newValue)
|
||||
{
|
||||
gamepad[keyCode]->setPosition(newValue);
|
||||
|
||||
RBX::InputObject::UserInputState currentState = RBX::InputObject::INPUT_STATE_CHANGE;
|
||||
if (newValue == G3D::Vector3::zero())
|
||||
{
|
||||
currentState = RBX::InputObject::INPUT_STATE_END;
|
||||
}
|
||||
else if (newValue.z >= 1.0f)
|
||||
{
|
||||
currentState = RBX::InputObject::INPUT_STATE_BEGIN;
|
||||
}
|
||||
|
||||
gamepad[keyCode]->setDelta(newValue - lastPos);
|
||||
gamepad[keyCode]->setInputState(currentState);
|
||||
|
||||
inputService->dangerousFireInputEvent(gamepad[keyCode], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateVibration();
|
||||
}
|
||||
|
||||
void XboxGameController::fireGamepadConnectedEvent(const int controllerIndex)
|
||||
{
|
||||
if (shared_ptr<RBX::DataModel> sharedDM = dataModel.lock())
|
||||
{
|
||||
if (RBX::UserInputService* inputService = RBX::ServiceProvider::find<RBX::UserInputService>(sharedDM.get()))
|
||||
{
|
||||
inputService->safeFireGamepadConnected(RBX::GamepadService::getGamepadEnumForInt(controllerIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XboxGameController::fireGamepadDisconnectedEvent(const int controllerIndex)
|
||||
{
|
||||
if (shared_ptr<RBX::DataModel> sharedDM = dataModel.lock())
|
||||
{
|
||||
if (RBX::UserInputService* inputService = RBX::ServiceProvider::find<RBX::UserInputService>(sharedDM.get()))
|
||||
{
|
||||
inputService->safeFireGamepadDisconnected(RBX::GamepadService::getGamepadEnumForInt(controllerIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64 XboxGameController::getGamepadGuid(const InputObject::UserInputType rbxGamepadId)
|
||||
{
|
||||
return gamepadConnectedMap[RBX::GamepadService::getGamepadIntForEnum(rbxGamepadId)].guid;
|
||||
}
|
||||
|
||||
void XboxGameController::findAvailableGamepadKeyCodesAndSet(RBX::InputObject::UserInputType gamepadType)
|
||||
{
|
||||
if (shared_ptr<RBX::DataModel> sharedDM = dataModel.lock())
|
||||
{
|
||||
if (RBX::UserInputService* inputService = RBX::ServiceProvider::find<RBX::UserInputService>(sharedDM.get()))
|
||||
{
|
||||
shared_ptr<RBX::Reflection::ValueArray> availableGamepadKeyCodes(new RBX::Reflection::ValueArray());
|
||||
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONA);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONB);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONX);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONY);
|
||||
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONL1);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONR1);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONL2);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONR2);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONL3);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONR3);
|
||||
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_THUMBSTICK1);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_THUMBSTICK2);
|
||||
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_DPADUP);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_DPADDOWN);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_DPADLEFT);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_DPADRIGHT);
|
||||
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONSELECT);
|
||||
availableGamepadKeyCodes->push_back(RBX::SDLK_GAMEPAD_BUTTONSTART);
|
||||
|
||||
inputService->setSupportedGamepadKeyCodes(gamepadType, availableGamepadKeyCodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XboxGameController::setVibrationMotorsEnabled(RBX::InputObject::UserInputType gamepadType)
|
||||
{
|
||||
if (gamepadType != InputObject::TYPE_GAMEPAD1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (shared_ptr<DataModel> dm = dataModel.lock())
|
||||
{
|
||||
if ( RBX::HapticService* hapticService = RBX::ServiceProvider::create<HapticService>(dm.get()) )
|
||||
{
|
||||
hapticService->setEnabledVibrationMotors(gamepadType, RBX::HapticService::MOTOR_LARGE, true);
|
||||
hapticService->setEnabledVibrationMotors(gamepadType, RBX::HapticService::MOTOR_SMALL, true);
|
||||
hapticService->setEnabledVibrationMotors(gamepadType, RBX::HapticService::MOTOR_LEFTTRIGGER, true);
|
||||
hapticService->setEnabledVibrationMotors(gamepadType, RBX::HapticService::MOTOR_RIGHTTRIGGER, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
void XboxGameController::setVibrationMotor(RBX::InputObject::UserInputType gamepadType, RBX::HapticService::VibrationMotor vibrationMotor, shared_ptr<const RBX::Reflection::Tuple> args)
|
||||
{
|
||||
if (!args)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (args->values.size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (gamepadType == InputObject::TYPE_GAMEPAD1)
|
||||
{
|
||||
float newMotorValue = 0.0f;
|
||||
RBX::Reflection::Variant newValue = args->values[0];
|
||||
if (newValue.isFloat())
|
||||
{
|
||||
newMotorValue = newValue.get<float>();
|
||||
newMotorValue = G3D::clamp(newMotorValue, 0.0f, 1.0f);
|
||||
}
|
||||
else // no valid number in first position, lets bail
|
||||
{
|
||||
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "First value to HapticService:SetMotor is not a valid number (must be a number between 0-1)");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (vibrationMotor)
|
||||
{
|
||||
case RBX::HapticService::MOTOR_LARGE:
|
||||
currentVibration.LeftMotorLevel = newMotorValue;
|
||||
break;
|
||||
case RBX::HapticService::MOTOR_SMALL:
|
||||
currentVibration.RightMotorLevel = newMotorValue;
|
||||
break;
|
||||
case RBX::HapticService::MOTOR_LEFTTRIGGER:
|
||||
currentVibration.LeftTriggerLevel = newMotorValue;
|
||||
break;
|
||||
case RBX::HapticService::MOTOR_RIGHTTRIGGER:
|
||||
currentVibration.RightTriggerLevel = newMotorValue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "boost\weak_ptr.hpp"
|
||||
#include "boost\shared_ptr.hpp"
|
||||
|
||||
#include "ControllerBuffer.h"
|
||||
|
||||
#include "util/KeyCode.h"
|
||||
#include "Util/G3DCore.h"
|
||||
|
||||
|
||||
#include "V8Tree/Service.h"
|
||||
#include "v8datamodel/InputObject.h"
|
||||
#include "v8datamodel/HapticService.h"
|
||||
|
||||
using namespace Windows::Xbox::Input;
|
||||
using namespace Windows::Xbox::System;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class DataModel;
|
||||
class GamepadService;
|
||||
class UserInputService;
|
||||
}
|
||||
|
||||
class XboxGameController
|
||||
{
|
||||
friend class XboxGameController;
|
||||
struct ControllerInfo {
|
||||
bool isConnected;
|
||||
uint64 guid;
|
||||
};
|
||||
private:
|
||||
XboxGameController() {} //private because you should always be setting a dm when creating
|
||||
|
||||
boost::weak_ptr<RBX::DataModel> dataModel;
|
||||
boost::shared_ptr<ControllerBuffer> controllerBuffer;
|
||||
|
||||
rbx::signals::scoped_connection getSupportedGamepadKeyCodesConnection;
|
||||
rbx::signals::scoped_connection updateInputConnection;
|
||||
|
||||
rbx::signals::scoped_connection setEnabledVibrationMotorsConnection;
|
||||
rbx::signals::scoped_connection setVibrationMotorConnection;
|
||||
|
||||
Windows::Foundation::EventRegistrationToken gamepadAddedEvent;
|
||||
Windows::Foundation::EventRegistrationToken gamepadRemovedEvent;
|
||||
|
||||
std::map<int,ControllerInfo> gamepadConnectedMap;
|
||||
|
||||
std::map<std::pair<Platform::String^, unsigned long long>, RBX::InputObject::UserInputType> xboxLiveIdToRBXGamepadType;
|
||||
|
||||
bool initializedControllerGuidMap;
|
||||
|
||||
GamepadVibration currentVibration;
|
||||
|
||||
void fireGamepadConnectedEvent(const int controllerIndex);
|
||||
void fireGamepadDisconnectedEvent(const int controllerIndex);
|
||||
|
||||
void handleNewGamepad(IGamepad^ gamepad);
|
||||
void handleGamepadRemove(IGamepad^ gamepad);
|
||||
|
||||
void processControllerBuffer();
|
||||
void updateVibration();
|
||||
|
||||
void findAvailableGamepadKeyCodesAndSet(RBX::InputObject::UserInputType gamepadType);
|
||||
|
||||
bool getRBXGamepadFromXboxLiveId(Platform::String^ xboxLiveId, unsigned long long controllerId, RBX::InputObject::UserInputType& gamepadEnum, int& gamepadInt);
|
||||
|
||||
void setVibrationMotorsEnabled(RBX::InputObject::UserInputType gamepadType);
|
||||
|
||||
public:
|
||||
XboxGameController(boost::shared_ptr<RBX::DataModel> newDM, shared_ptr<ControllerBuffer> newControllerBuffer);
|
||||
|
||||
void setDataModel(boost::shared_ptr<RBX::DataModel> newDM);
|
||||
|
||||
void setupGamepadHandling();
|
||||
void resetControllerGuidMap();
|
||||
|
||||
uint64 getGamepadGuid(const RBX::InputObject::UserInputType rbxGamepadId);
|
||||
|
||||
int getRbxGamepadIntFromGamepad(Windows::Xbox::Input::IGamepad^ gamepad);
|
||||
void remapXboxLiveIdToRBXGamepadId(Platform::String^ xboxLiveId, unsigned long long controllerId, RBX::InputObject::UserInputType gamepadType);
|
||||
|
||||
void setVibrationMotor(RBX::InputObject::UserInputType gamepadType, RBX::HapticService::VibrationMotor vibrationMotor, shared_ptr<const RBX::Reflection::Tuple> args);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
#include "v8datamodel/PlatformService.h"
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include "VoiceChatBase.h"
|
||||
using namespace Microsoft::Xbox::Services;
|
||||
using namespace Microsoft::Xbox::Services::Multiplayer;
|
||||
namespace RBX
|
||||
{
|
||||
class VoiceChatBase;
|
||||
}
|
||||
|
||||
class XboxMultiplayerManager: public enable_shared_from_this <XboxMultiplayerManager>
|
||||
{
|
||||
public:
|
||||
XboxMultiplayerManager(XboxLiveContext^ xblContext, GUID newPlayerSessionGUID,int newRobloxUserId = -1 );
|
||||
~XboxMultiplayerManager();
|
||||
|
||||
void startMultiplayerListeners();
|
||||
void stopMultiplayerListeners();
|
||||
|
||||
void joinMultiplayerSession(Platform::String^ handleID, Platform::String^ xuid);
|
||||
|
||||
void createGameSession(int pmpCreatorId = 0);
|
||||
void joinGameSession(Platform::String^ gameSessionUri);
|
||||
void leaveGameSession();
|
||||
|
||||
WriteSessionResult^ createPartySession();
|
||||
void joinPartySession(Platform::String^ partySessionUri);
|
||||
void leavePartySession();
|
||||
|
||||
MultiplayerSession^ createNewDefaultSession( Platform::String^ templateName, Platform::String^ sessionName, int pmpCreatorId = 0);
|
||||
|
||||
void sendGameInvite();
|
||||
|
||||
void setTeamChannel(unsigned char channelId);
|
||||
|
||||
void recentPlayersList();
|
||||
|
||||
void getInGamePlayers(shared_ptr<RBX::Reflection::ValueArray> values);
|
||||
int requestJoinParty();
|
||||
|
||||
|
||||
void updatePlayer(unsigned int rbxId, float distance, RBX::VoiceChatBase::TalkingDelta* talkingDeltaOut);
|
||||
void muteVoiceChatPlayer(unsigned rbxId);
|
||||
void unmuteVoiceChatPlayer(unsigned rbxId);
|
||||
unsigned voiceChatGetState(int userId);
|
||||
|
||||
void xbEventMultiplayerRoundStart();
|
||||
void xbEventMultiplayerRoundEnd();
|
||||
int getLocalRobloxUserId() {return localRobloxUserId;}
|
||||
|
||||
int getPMPCreatorId();
|
||||
|
||||
static bool parseFromXboxCustomJson(Platform::String^ jsonStr,std::string str, int* out);
|
||||
private:
|
||||
XboxLiveContext^ xboxLiveContext;
|
||||
MultiplayerSession^ gameSession;
|
||||
MultiplayerSession^ partySession;
|
||||
|
||||
GUID playerSessionGuid;
|
||||
|
||||
RBX::Timer<RBX::Time::Precise> gametimer;
|
||||
|
||||
Windows::Foundation::EventRegistrationToken sessionChangeToken;
|
||||
Windows::Foundation::EventRegistrationToken subscriptionLostToken;
|
||||
|
||||
int localRobloxUserId;
|
||||
|
||||
RBX::mutex stateLock;
|
||||
RBX::mutex processLock;
|
||||
RBX::mutex taskLock;
|
||||
RBX::mutex voiceUsersLock;
|
||||
|
||||
bool expectedLeaveSession;
|
||||
unsigned long long lastProcessedGameChange;
|
||||
|
||||
void onSessionChanged( Platform::Object^ object, RealTimeActivity::RealTimeActivityMultiplayerSessionChangeEventArgs^ arg );
|
||||
void processSessionDeltas( MultiplayerSession^ currentSession, MultiplayerSession^ previousSession, unsigned long long lastChange );
|
||||
void onSubscriptionLost( Platform::Object^ object, RealTimeActivity::RealTimeActivityMultiplayerSubscriptionsLostEventArgs^ arg );
|
||||
|
||||
//event MultiplayerStateChanged^ OnMultiplayerStateChanged;
|
||||
void processSessionChange( MultiplayerSession^ session );
|
||||
WriteSessionResult^ updateAndProcessSession( XboxLiveContext^ context, MultiplayerSession^ session, MultiplayerSessionWriteMode writeMode );
|
||||
WriteSessionResult^ writeSession( XboxLiveContext^ context, MultiplayerSession^ session, MultiplayerSessionWriteMode writeMode );
|
||||
|
||||
inline bool isMultiplayerSessionChangeTypeSet( MultiplayerSessionChangeTypes value, MultiplayerSessionChangeTypes check )
|
||||
{ return (value & check) == check; }
|
||||
|
||||
boost::shared_ptr<RBX::VoiceChatBase> voiceChat;
|
||||
|
||||
typedef boost::unordered_map<unsigned, unsigned> RbxIdToSessionIdMap;
|
||||
RbxIdToSessionIdMap rbxIdToSessionId;
|
||||
boost::unordered_set<unsigned> mutedUsersRbxIds; // Why is this here? It is to keep list of muted user while you play the game. We don't like it here too.
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
#pragma once
|
||||
|
||||
#include "V8Tree/Instance.h"
|
||||
#include "V8Tree/Service.h"
|
||||
#include "boost\weak_ptr.hpp"
|
||||
#include "XbLiveUtils.h"
|
||||
#include "XboxGameController.h"
|
||||
#include "XboxMultiplayerManager.h"
|
||||
#include "v8datamodel/InputObject.h"
|
||||
#include "v8datamodel/PlatformService.h"
|
||||
#include "v8datamodel/TeleportCallback.h"
|
||||
|
||||
using namespace Windows::Xbox::Input;
|
||||
using namespace Windows::Xbox::System;
|
||||
using namespace Windows::Networking::Connectivity;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Microsoft::Xbox::Services;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class DataModel;
|
||||
class DataModelJob;
|
||||
class Verb;
|
||||
class Game;
|
||||
class ViewBase;
|
||||
}
|
||||
|
||||
class Marshaller;
|
||||
class RenderJob;
|
||||
class ControllerBuffer;
|
||||
class UserIDTranslator;
|
||||
class KeyboardProvider;
|
||||
|
||||
class XboxPlatform: public RBX::IPlatformAPI, public RBX::TeleportCallback
|
||||
{
|
||||
public:
|
||||
XboxPlatform();
|
||||
virtual ~XboxPlatform(); // not that it matters...
|
||||
|
||||
void onProtocolActivated( Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs^ args );
|
||||
void onWindowActivatedChanged(Windows::UI::Core::WindowActivatedEventArgs^ args);
|
||||
|
||||
void tick(); // called by main
|
||||
std::pair<unsigned, unsigned> getRenderSize() const;
|
||||
void requestGameShutdown(bool teleport) override; // called by exit verb
|
||||
|
||||
void sendConsumeAllRequest(bool is_purchase);
|
||||
void returnToEngagementScreen(RBX::ReturnToEngageScreenStatus status);
|
||||
void attemptToSignActiveUserIn(std::string userToSignIn, IController^ controllerToUse);
|
||||
|
||||
void startEventHandlers();
|
||||
void stopEventHandlers();
|
||||
|
||||
void suspendViewXbox();
|
||||
void resumeViewXbox();
|
||||
void onNormalResume();
|
||||
|
||||
void xbEventPlayerSessionStart();
|
||||
void xbEventPlayerSessionEnd();
|
||||
void xbEventPlayerSessionPause();
|
||||
void xbEventPlayerSessionResume();
|
||||
|
||||
virtual void voiceChatSetMuteState(int userId, bool mute);
|
||||
virtual unsigned voiceChatGetState(int userId);
|
||||
|
||||
User^ currentUser;
|
||||
IController^ currentController;
|
||||
Microsoft::Xbox::Services::XboxLiveContext^ xboxLiveContext;
|
||||
|
||||
private:
|
||||
|
||||
virtual RBX::AccountAuthResult performAuthorization(RBX::InputObject::UserInputType gamepadId, bool unLinkedCheck) override;
|
||||
|
||||
virtual RBX::GameStartResult startGame3( RBX::GameJoinType gt, int id );
|
||||
virtual int netConnectionCheck() override;
|
||||
virtual void setScreenResolution(double px, double py) override;
|
||||
virtual int fetchFriends(RBX::InputObject::UserInputType gamepadId, std::string* result);
|
||||
virtual int popupHelpUI();
|
||||
virtual int launchPlatformUri(const std::string baseUri);
|
||||
virtual void popupGameInviteUI();
|
||||
virtual int popupPartyUI(RBX::InputObject::UserInputType gamepadId);
|
||||
virtual int popupProfileUI(RBX::InputObject::UserInputType gamepadId, std::string xuid);
|
||||
virtual int popupAccountPickerUI(RBX::InputObject::UserInputType gamepadId);
|
||||
virtual int getPMPCreatorId();
|
||||
virtual int getTitleId();
|
||||
virtual shared_ptr<const RBX::Reflection::ValueTable> getVersionIdInfo();
|
||||
virtual void showKeyBoard(std::string& title, std::string& description, std::string& defaultText, unsigned keyboardType, RBX::DataModel* dm);
|
||||
|
||||
virtual shared_ptr<const RBX::Reflection::ValueTable> getPlatformUserInfo() override;
|
||||
|
||||
// linking functions
|
||||
virtual int performAccountLink(const std::string& accountName, const std::string& password, std::string* response) override;
|
||||
virtual int performUnlinkAccount(std::string* response) override;
|
||||
virtual int performSetRobloxCredentials(const std::string& accountName, const std::string& password, std::string* response) override;
|
||||
virtual RBX::AccountAuthResult performHasRobloxCredentials() override;
|
||||
virtual RBX::AccountAuthResult performHasLinkedAccount() override;
|
||||
|
||||
virtual int fetchInventoryInfo(shared_ptr<RBX::Reflection::ValueArray> values);
|
||||
virtual int fetchCatalogInfo(shared_ptr<RBX::Reflection::ValueArray> values);
|
||||
virtual RBX::PlatformPurchaseResult requestPurchase(const std::string& productId);
|
||||
|
||||
virtual int getPlatformPartyMembers(shared_ptr<RBX::Reflection::ValueArray> result);
|
||||
virtual int getInGamePlayers(shared_ptr<RBX::Reflection::ValueArray> result);
|
||||
|
||||
virtual RBX::AwardResult awardAchievement(const std::string& eventName) override;
|
||||
virtual RBX::AwardResult setHeroStat(const std::string& eventName, double* value) override;
|
||||
|
||||
// Teleport support
|
||||
virtual void doTeleport(const std::string& url, const std::string& ticket, const std::string& script) override;
|
||||
virtual bool isTeleportEnabled() const override;
|
||||
virtual std::string xBox_getGamerTag() const override;
|
||||
private:
|
||||
bool activated; // whether or not the window is in focus
|
||||
bool trySignInOnActivation; // set to true when user is logged out and needs to be relogged in
|
||||
|
||||
Marshaller* marshaller; // thread marshaller
|
||||
RBX::Game* intro; // main menu
|
||||
RBX::Game* game; // game being played (or null)
|
||||
RBX::Game* current; // what the view is bound to, points to either *intro or *game
|
||||
RBX::ViewBase* view; // renderer
|
||||
RBX::Verb* exitVerb; // handles game exit from ESC menu
|
||||
RBX::mutex gameMutex; // use this mutex if modifying game or exitVerb
|
||||
volatile long gameShutdownGuard; // prevents shutdown requests from piling up
|
||||
|
||||
// makes sure we don't miss input during low FPS
|
||||
shared_ptr<ControllerBuffer> controllerBuffer;
|
||||
|
||||
shared_ptr<RenderJob> renderJob;
|
||||
shared_ptr<XboxGameController> controller;
|
||||
shared_ptr<UserIDTranslator> userTranslator;
|
||||
shared_ptr<RBX::DataModelJob> partyPoller;
|
||||
shared_ptr<RBX::DataModelJob> validSessionPoller;
|
||||
shared_ptr<RBX::DataModelJob> voiceChatPoller;
|
||||
|
||||
scoped_ptr<KeyboardProvider> keyboard;
|
||||
shared_ptr<XboxMultiplayerManager> multiplayerManager;
|
||||
|
||||
struct PartyStatus
|
||||
{
|
||||
std::string currentPartyGuid;
|
||||
std::string pollPartyGuid;
|
||||
bool leaderInGame;
|
||||
DWORD lastCheckTime;
|
||||
bool tried;
|
||||
PartyStatus(): lastCheckTime(0), leaderInGame(false), tried(false) {}
|
||||
}
|
||||
partyStatus;
|
||||
|
||||
struct GameInviteHandle{
|
||||
std::string targetUserXuid;
|
||||
int followUserId;
|
||||
int pmpCreator; // used for Play My Place
|
||||
GameInviteHandle(): followUserId(-1), targetUserXuid("") {}
|
||||
}
|
||||
gameInviteHandle;
|
||||
|
||||
void initVoiceChatPoller();
|
||||
void removeVoiceChatPoller();
|
||||
|
||||
GUID playerSessionGuid;// used by xbox to track user session info
|
||||
|
||||
void setPartyPollStatus(const std::string& newGuid, bool leaderIn ); // can only be called on marshaller thread
|
||||
void setPartyCreateStatus( const std::string& current ); // can only be called on marshaller thread
|
||||
void setPartyTriedStatus();
|
||||
void handlePartyJoin(); // can only be called on marshaller thread
|
||||
void initPartyPoller();
|
||||
void removePartyPoller();
|
||||
RBX::GameStartResult prepareGameLaunch( std::string& authenticationUrl, std::string& authenticationTicket, std::string& scriptUrl, RBX::GameJoinType joinType, int id ); // fetches auth, ticket, script
|
||||
RBX::GameStartResult initGame( const std::string& authUrl, const std::string& ticket, const std::string joinScriptUrl );
|
||||
|
||||
void switchView( RBX::Game* to ); // 0 to unbind from both
|
||||
|
||||
// these are called when we gain/lose focus of our window
|
||||
void onActivated();
|
||||
void onDeactivated();
|
||||
|
||||
volatile long returnToEngagementFlag;
|
||||
volatile long consumeAllFlag;
|
||||
|
||||
RBX::mutex controllerPairingMutex;
|
||||
|
||||
void storeLocalGamerPicture(XboxLiveContext^ xboxLiveContext);
|
||||
|
||||
void checkValidSessionAsync(); // return to engagement screen if fails
|
||||
void initValidSessionPoller();
|
||||
void removeValidSessionPoller();
|
||||
|
||||
void initializeAuthEventHandlers();
|
||||
void initializeNetworkConnectionEventHandlers();
|
||||
|
||||
void sampleInput();
|
||||
void startControllerBuffering();
|
||||
void endControllerBuffering();
|
||||
|
||||
User^ tryToSignInActiveUser(IController ^ controllerToUse);
|
||||
void attemptToReconnectController(std::string userToSignIn);
|
||||
|
||||
User^ getActiveUser();
|
||||
|
||||
std::vector<IGamepad^> getGamepadsForCurrentUser();
|
||||
void handleCurrentControllerChanged();
|
||||
|
||||
std::vector< Marketplace::CatalogItemDetails^ > catalogItemDetails;
|
||||
|
||||
bool setRichPresence();
|
||||
void getCatalogInfo();
|
||||
bool purchaseItem( const std::wstring& wstrSignedOffer );
|
||||
void requestGameShutdown_nolock(bool teleport);
|
||||
bool checkXboxPrivileges(unsigned privilegeID);
|
||||
void setGameProgress();
|
||||
|
||||
void shutdownDataModels();
|
||||
|
||||
void fireGameJoinedEvent(RBX::GameStartResult); // wrapper for firing game joined event
|
||||
|
||||
RBX::AccountAuthResult xboxAuthHttpHelper( const std::string& url, std::string* response, bool isPost);
|
||||
RBX::AccountAuthResult authAndInitUser();
|
||||
};
|
||||
@@ -0,0 +1,225 @@
|
||||
#include "XboxUtils.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "v8xml/WebParser.h"
|
||||
#include "RobloxServicesTools.h"
|
||||
#include "v8datamodel/FastLogSettings.h"
|
||||
#include "v8datamodel/DataModel.h"
|
||||
#include "util/http.h"
|
||||
|
||||
#include "v8datamodel/DataModelJob.h"
|
||||
|
||||
#define XBOX_APP_SETTINGS_STRING "XboxAppSettings"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// fast flags
|
||||
|
||||
bool loadLocalFFlags()
|
||||
{
|
||||
std::string s;
|
||||
std::copy( std::istream_iterator<char>( std::ifstream("fflags.json") ), std::istream_iterator<char>(), std::back_inserter(s) );
|
||||
LoadClientSettingsFromString( CLIENT_APP_SETTINGS_STRING, s, &RBX::ClientAppSettings::singleton() );
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string getFFlagUrl( std::string baseUrl, std::string group, std::string apiKey)
|
||||
{
|
||||
auto pos = baseUrl.find_first_of("://www.");
|
||||
if( pos == std::string::npos )
|
||||
{
|
||||
RBXASSERT( !"baseurl is missing ://www." ); // rewrite the function!
|
||||
return "";
|
||||
}
|
||||
|
||||
baseUrl.replace( pos, 7, "://clientsettings.api." );
|
||||
|
||||
return RBX::format( "%sSetting/QuietGet/%s/?apiKey=%s", baseUrl.c_str(), group.c_str(), apiKey.c_str() );
|
||||
}
|
||||
|
||||
bool fetchFFlags(const char* baseUrl)
|
||||
{
|
||||
std::string fflagsClient;
|
||||
std::string fflagsXbox;
|
||||
|
||||
std::string fflagClientUrl = getFFlagUrl(baseUrl, CLIENT_APP_SETTINGS_STRING, CLIENT_SETTINGS_API_KEY);
|
||||
std::string fflagXboxUrl = getFFlagUrl(baseUrl, XBOX_APP_SETTINGS_STRING, CLIENT_SETTINGS_API_KEY);
|
||||
|
||||
enum { kSleepMs = 100, kTimeoutMs = 30000 };
|
||||
try
|
||||
{
|
||||
RBX::HttpFuture result1 = RBX::HttpAsync::get(fflagClientUrl);
|
||||
RBX::HttpFuture result2 = RBX::HttpAsync::get(fflagXboxUrl);
|
||||
for( int j=0; ; j += kSleepMs )
|
||||
{
|
||||
if( j>kTimeoutMs )
|
||||
{
|
||||
dprintf("FFlag fetch timeout\n");
|
||||
return false; // timeout
|
||||
}
|
||||
|
||||
if( result1.get_state() == boost::future_state::ready &&
|
||||
result2.get_state() == boost::future_state::ready )
|
||||
{
|
||||
fflagsClient = result1.get();
|
||||
fflagsXbox = result2.get();
|
||||
break;
|
||||
}
|
||||
|
||||
::SleepEx(kSleepMs, TRUE);
|
||||
}
|
||||
|
||||
if( fflagsClient.empty() || fflagsXbox.empty())
|
||||
{
|
||||
dprintf("FFlag fetch failed!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadClientSettingsFromString( CLIENT_APP_SETTINGS_STRING, result1.get(), &RBX::ClientAppSettings::singleton() );
|
||||
LoadClientSettingsFromString( XBOX_APP_SETTINGS_STRING, result2.get(), &RBX::ClientAppSettings::singleton() );
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
dprintf("FFlag fetch exception: %s\n", e.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// join script
|
||||
|
||||
|
||||
static std::string readStringValue(shared_ptr<const RBX::Reflection::ValueTable> jsonResult, std::string name)
|
||||
{
|
||||
RBX::Reflection::ValueTable::const_iterator itData = jsonResult->find(name);
|
||||
if (itData != jsonResult->end())
|
||||
{
|
||||
return itData->second.get<std::string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(RBX::format("Unexpected string result for %s", name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static int readIntValue(shared_ptr<const RBX::Reflection::ValueTable> jsonResult, std::string name)
|
||||
{
|
||||
RBX::Reflection::ValueTable::const_iterator itData = jsonResult->find(name);
|
||||
if (itData != jsonResult->end())
|
||||
{
|
||||
return itData->second.get<int>();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(RBX::format("Unexpected int result for %s", name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
PlaceLauncherResult requestPlaceInfo(const std::string url, std::string& authenticationUrl, std::string& ticket, std::string& scriptUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string response;
|
||||
RBX::Http(url).get(response);
|
||||
|
||||
std::stringstream jsonStream;
|
||||
jsonStream << response;
|
||||
shared_ptr<const RBX::Reflection::ValueTable> jsonResult(rbx::make_shared<const RBX::Reflection::ValueTable>());
|
||||
bool parseResult = RBX::WebParser::parseJSONTable(jsonStream.str(), jsonResult);
|
||||
if (!parseResult)
|
||||
return PlaceLaunch_SomethingReallyBad;
|
||||
|
||||
int status = readIntValue(jsonResult, "status");
|
||||
if (status != PlaceLaunch_Joining)
|
||||
return (PlaceLauncherResult)status;
|
||||
|
||||
authenticationUrl = readStringValue(jsonResult, "authenticationUrl");
|
||||
ticket = readStringValue(jsonResult, "authenticationTicket");
|
||||
scriptUrl = readStringValue(jsonResult, "joinScriptUrl");
|
||||
return PlaceLaunch_Joining;
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
dprintf( "Exception when requesting place info: %s. ", e.what() );
|
||||
}
|
||||
return PlaceLaunch_SomethingReallyBad;
|
||||
}
|
||||
|
||||
// Case-sensitive comparison of two Platform::String^ objects
|
||||
bool isStringEqual(Platform::String^ val1,Platform::String^ val2)
|
||||
{
|
||||
return (wcscmp(val1->Data(), val2->Data() ) == 0);
|
||||
}
|
||||
|
||||
// Case-insensitive comparison of two Platform::String^ objects
|
||||
bool isStringEqualCaseInsensitive(Platform::String^ val1,Platform::String^ val2)
|
||||
{
|
||||
return (_wcsicmp(val1->Data(), val2->Data()) == 0);
|
||||
}
|
||||
|
||||
std::string ws2s(const wchar_t* data)
|
||||
{
|
||||
// NOTE: had to hack it because std::codecvt is so BAD, it throws up if it encounters a unicode symbol
|
||||
std::string result;
|
||||
if(data)
|
||||
{
|
||||
for (const wchar_t* p = data; *p; ++p)
|
||||
if ((unsigned)*p<128)
|
||||
result.push_back(*p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring s2ws(const std::string* data)
|
||||
{
|
||||
std::wstring ws(data->begin(), data->end());
|
||||
return ws;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct GenericDataModelJob : RBX::DataModelJob
|
||||
{
|
||||
double per;
|
||||
std::function<void()> stepfn;
|
||||
|
||||
GenericDataModelJob( const char* name, int type, shared_ptr< RBX::DataModel> dm, double period, std::function<void()> step )
|
||||
: RBX::DataModelJob(name, (TaskType)type, false, dm, RBX::Time::Interval(period) )
|
||||
{
|
||||
per = period;
|
||||
stepfn = step;
|
||||
}
|
||||
|
||||
virtual RBX::TaskScheduler::StepResult stepDataModelJob(const Stats& stats) override
|
||||
{
|
||||
if( stepfn ) stepfn();
|
||||
return RBX::TaskScheduler::Stepped;
|
||||
}
|
||||
|
||||
virtual RBX::Time::Interval sleepTime(const Stats& stats) override
|
||||
{
|
||||
return computeStandardSleepTime(stats, 1/per);
|
||||
}
|
||||
virtual RBX::TaskScheduler::Job::Error error(const Stats& stats) override
|
||||
{
|
||||
return computeStandardError(stats, 1/per);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
shared_ptr<RBX::DataModelJob> addGenericDataModelJob( const char* name, int type, shared_ptr< RBX::DataModel> dm, double period, std::function<void()> step )
|
||||
{
|
||||
shared_ptr< RBX::DataModelJob > job ( new GenericDataModelJob(name, type, dm, period, step) );
|
||||
RBX::TaskScheduler::singleton().add( job );
|
||||
return job;
|
||||
}
|
||||
|
||||
void removeGenericDataModelJob( shared_ptr< RBX::DataModelJob >& job, std::function<void()> spinfunc )
|
||||
{
|
||||
if( !job ) return;
|
||||
RBX::TaskScheduler::singleton().removeBlocking(job, [=]()->void { spinfunc(); } );
|
||||
job.reset();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
// in this file...
|
||||
// - fast flag fetching helpers
|
||||
// - join script fetching/parsing helpers
|
||||
// - unicode conversion
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <functional>
|
||||
#include "rbx/Boost.hpp"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class DataModel;
|
||||
class DataModelJob;
|
||||
}
|
||||
|
||||
|
||||
void dprintf( const char* fmt, ... );
|
||||
|
||||
bool loadLocalFFlags();
|
||||
|
||||
bool fetchFFlags(const char* baseUrl);
|
||||
|
||||
// status codes
|
||||
enum PlaceLauncherResult
|
||||
{
|
||||
PlaceLaunch_Waiting = 0, // wait
|
||||
PlaceLaunch_Loading = 1, // wait
|
||||
PlaceLaunch_Joining = 2, // *the* success code!
|
||||
PlaceLaunch_Disabled = 3,
|
||||
PlaceLaunch_Error = 4,
|
||||
PlaceLaunch_GameEnded = 5,
|
||||
PlaceLaunch_GameFull = 6,
|
||||
PlaceLaunch_UserLeft = 10,
|
||||
PlaceLaunch_Restricted = 11,
|
||||
PlaceLaunch_Unauthorized = 12,
|
||||
|
||||
PlaceLaunch_SomethingReallyBad = 1000, // not a real status
|
||||
};
|
||||
|
||||
|
||||
PlaceLauncherResult requestPlaceInfo(const std::string url, std::string& authenticationUrl, std::string& ticket, std::string& scriptUrl);
|
||||
|
||||
bool isStringEqual(Platform::String^ val1,Platform::String^ val2);
|
||||
bool isStringEqualCaseInsensitive(Platform::String^ val1,Platform::String^ val2);
|
||||
|
||||
std::string ws2s(const wchar_t* data);
|
||||
std::wstring s2ws(const std::string* data);
|
||||
|
||||
template< class M >
|
||||
inline typename M::mapped_type* mapget( M& m, const typename M::key_type& k )
|
||||
{
|
||||
M::iterator it = m.find(k);
|
||||
if( it == m.end() ) return 0;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
shared_ptr< RBX::DataModelJob > addGenericDataModelJob( const char* name, int type, shared_ptr< RBX::DataModel> dm, double period, std::function<void()> step );
|
||||
|
||||
void removeGenericDataModelJob( shared_ptr< RBX::DataModelJob >& job, std::function<void()> spinfunc );
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
#include "async.h"
|
||||
|
||||
namespace AsyncDetail
|
||||
{
|
||||
extern int g_asyncLeaked = 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Many xbox functions return an IAsyncOperation<T> and leave you to deal with it.
|
||||
// This dirty little helper can take away some of the pain.
|
||||
//
|
||||
// Example: suppose you want to call this monstrosity: IAsyncOperation<ISomeResult^>^ SomeXboxClass::SomeAsyncMethod();
|
||||
//
|
||||
// Use case #1: get/subscribe/wait
|
||||
//
|
||||
// async( var->SomeXBoxMethod() ).complete( // - install the 'completed' event handler
|
||||
// [<captures>]( ISomeResult^ result )
|
||||
// {
|
||||
// <your-completed-handler-goes-here>;
|
||||
// }
|
||||
// ).except( [<captures>]( Platform::Exception^ e ) // - install the exception handler.
|
||||
// { // if you don't do that, join() will rethrow the exception on the calling thread
|
||||
// <your-exception-handler-goes-here>;
|
||||
// }
|
||||
// ).join(); // - wait until it finishes
|
||||
//
|
||||
// Use case #2: several parallel operations:
|
||||
//
|
||||
// auto& a1 = async( var->SomeXBoxMethod() ).complete( <same handler> ).except( .... ); // do not call join yet
|
||||
// auto& a2 = async( var2->SomeOtherMethod() ).complete( <handler> ).except( .... ); // neither yet
|
||||
// <do stuff>;
|
||||
// a1.join(); a2.join();
|
||||
//
|
||||
// Use case #3: don't care to wait
|
||||
// async( var->SomeXBoxMethod() ).complete( <same handler> ).except( .... ).detach(); // will return immediately without waiting
|
||||
//
|
||||
// You MUST call either .join() or .detach() on *every* async instance you spawn, otherwise this will leak the instances.
|
||||
// g_asyncLeaked contains the number of async instances still alive.
|
||||
//
|
||||
|
||||
|
||||
#include <wrl.h>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
namespace AsyncDetail
|
||||
{
|
||||
|
||||
extern int g_asyncLeaked;
|
||||
|
||||
using Windows::Foundation::IAsyncAction;
|
||||
using Windows::Foundation::IAsyncOperation;
|
||||
using Windows::Foundation::AsyncStatus;
|
||||
|
||||
|
||||
// abstracts different Async Types,
|
||||
template< class Op > struct subscriber; // not implemented
|
||||
|
||||
template< class Ty > struct subscriber< IAsyncOperation<Ty> >
|
||||
{
|
||||
typedef Ty result_type;
|
||||
typedef IAsyncOperation<Ty> op_type;
|
||||
|
||||
static void subscribe( op_type^ op, std::function< void(op_type^, AsyncStatus) > fn )
|
||||
{
|
||||
op->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<result_type>( fn );
|
||||
}
|
||||
|
||||
template< class Fn >
|
||||
static void call_handler( op_type^ op, Fn& fn ) { fn( op->GetResults() ); }
|
||||
};
|
||||
|
||||
template<> struct subscriber<IAsyncAction>
|
||||
{
|
||||
typedef IAsyncAction op_type;
|
||||
|
||||
static void subscribe( op_type^ op, std::function< void(op_type^, AsyncStatus) > fn )
|
||||
{
|
||||
op->Completed = ref new Windows::Foundation::AsyncActionCompletedHandler( fn );
|
||||
}
|
||||
|
||||
template< class Fn >
|
||||
static void call_handler( op_type^ op, Fn& fn ) { op->GetResults(); fn(); }
|
||||
};
|
||||
|
||||
|
||||
template< class Ty, class Op >
|
||||
class async_caller
|
||||
{
|
||||
typedef async_caller<Ty, Op> myt;
|
||||
typedef std::function<void(Ty)> func_success;
|
||||
typedef std::function<void()> func_error;
|
||||
typedef Op opera;
|
||||
typedef Windows::Foundation::IAsyncAction action;
|
||||
typedef Platform::Exception exception;
|
||||
typedef std::function<void(exception^ e)> excfunc;
|
||||
|
||||
func_success m_fn_success;
|
||||
func_error m_fn_error;
|
||||
func_error m_fn_cancelled;
|
||||
opera^ m_op;
|
||||
exception ^m_xcpt;
|
||||
excfunc m_excfn;
|
||||
std::atomic<int> m_done;
|
||||
|
||||
public:
|
||||
static myt& create( opera^ op ) { return *new myt(op); }
|
||||
|
||||
myt& complete( func_success fn )
|
||||
{
|
||||
m_fn_success = fn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
myt& error( func_error fn )
|
||||
{
|
||||
m_fn_error = fn;
|
||||
return * this;
|
||||
}
|
||||
|
||||
myt& cancelled( func_error fn )
|
||||
{
|
||||
m_fn_cancelled = fn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
myt& except(excfunc fn)
|
||||
{
|
||||
m_excfn = fn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void join()
|
||||
{
|
||||
install_handlers();
|
||||
|
||||
while ( m_op->Status == Windows::Foundation::AsyncStatus::Started || !m_done.load() )
|
||||
{
|
||||
::Sleep(1);
|
||||
}
|
||||
if( m_xcpt )
|
||||
{
|
||||
if( m_excfn )
|
||||
m_excfn(m_xcpt);
|
||||
else
|
||||
{
|
||||
delete this;
|
||||
throw m_xcpt; // exception translation: if there was an exception on the async thread, this will make sure the exception propagates to the calling thread.
|
||||
}
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
void detach()
|
||||
{
|
||||
install_handlers();
|
||||
|
||||
if( m_excfn ) // nope!
|
||||
RBXASSERT( !"Do not install exception handlers if you're calling detach(). There is no meaningful exception translation possible in this case." );
|
||||
|
||||
int expect = 0;
|
||||
if( !m_done.compare_exchange_strong( expect, 1 ) ) // are we done yet?
|
||||
{
|
||||
delete this; // kill self, otherwise the handler will do that from its own context
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
async_caller( opera^ op ): m_op(op), m_done(0) { g_asyncLeaked++; }
|
||||
~async_caller() { g_asyncLeaked--; }
|
||||
async_caller(); // = delete; // use auto& a instead.
|
||||
async_caller( const myt& ); // = delete;
|
||||
void operator=(const myt&); // = delete;
|
||||
|
||||
void install_handlers()
|
||||
{
|
||||
subscriber<Op>::subscribe( m_op,
|
||||
[this](opera^ operation, AsyncStatus status) -> void
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Windows::Foundation::AsyncStatus::Canceled:
|
||||
if( m_fn_cancelled )
|
||||
m_fn_cancelled();
|
||||
break;
|
||||
case Windows::Foundation::AsyncStatus::Completed:
|
||||
if( m_fn_success )
|
||||
subscriber<Op>::call_handler( m_op, m_fn_success );
|
||||
break;
|
||||
case Windows::Foundation::AsyncStatus::Error:
|
||||
if( m_fn_error )
|
||||
m_fn_error();
|
||||
break;
|
||||
default:
|
||||
RBXASSERT(!"should not happen");
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch( exception^ e )
|
||||
{
|
||||
m_xcpt = e;
|
||||
}
|
||||
|
||||
int expect = 0;
|
||||
if( !m_done.compare_exchange_strong( expect, 1 ) )
|
||||
{
|
||||
delete this; // detach() has been already called, delete self and exit
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
} // ns AsyncDetail
|
||||
|
||||
template <class Ty>
|
||||
inline AsyncDetail::async_caller<Ty, Windows::Foundation::IAsyncOperation<Ty> >& async( Windows::Foundation::IAsyncOperation<Ty>^ op )
|
||||
{
|
||||
return AsyncDetail::async_caller<Ty, Windows::Foundation::IAsyncOperation<Ty> >::create(op);
|
||||
}
|
||||
|
||||
inline AsyncDetail::async_caller<void, Windows::Foundation::IAsyncAction>& async( Windows::Foundation::IAsyncAction^ op )
|
||||
{
|
||||
return AsyncDetail::async_caller<void, Windows::Foundation::IAsyncAction>::create(op);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,31 @@
|
||||
'''
|
||||
This small tool fetches fastflags from prod and prints to standard output.
|
||||
|
||||
Usage:
|
||||
|
||||
python fetchfflags.py
|
||||
|
||||
'''
|
||||
|
||||
import sys;
|
||||
import urllib;
|
||||
|
||||
test = "http://clientsettings.api.watrbx.wtf/Setting/QuietGet/ClientAppSettings/?apiKey=76E5A40C-3AE1-4028-9F10-7C62520BD94F"
|
||||
|
||||
BASE_SITE = "watrbx.wtf"
|
||||
#BASE_SITE = "gametest5.pizzaboxer.fun"
|
||||
API_KEY = "76E5A40C-3AE1-4028-9F10-7C62520BD94F"
|
||||
|
||||
def main():
|
||||
sys.stderr.write("Fetching fast flags...\n")
|
||||
url = "http://clientsettings.api.%s/Setting/QuietGet/ClientAppSettings/?apiKey=%s" % (BASE_SITE, API_KEY)
|
||||
result = urllib.urlopen(url)
|
||||
if result.getcode() != 200:
|
||||
sys.exit(1)
|
||||
try:
|
||||
for line in result:
|
||||
print line,
|
||||
finally:
|
||||
result.close()
|
||||
|
||||
main()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,276 @@
|
||||
#include <xdk.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <wrl.h>
|
||||
#include <ppltasks.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
|
||||
|
||||
#include "XboxService.h"
|
||||
#include "XboxUtils.h"
|
||||
#include "async.h"
|
||||
|
||||
#include "v8datamodel/GameSettings.h"
|
||||
#include "V8DataModel/GameBasicSettings.h"
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::ApplicationModel;
|
||||
using namespace Windows::ApplicationModel::Core;
|
||||
using namespace Windows::ApplicationModel::Activation;
|
||||
using namespace Windows::UI::Core;
|
||||
|
||||
using namespace Concurrency;
|
||||
|
||||
extern void dprintf( const char* fmt, ... );
|
||||
|
||||
FASTFLAGVARIABLE(ForceRetail, true);
|
||||
|
||||
ref class ViewProvider sealed : public Windows::ApplicationModel::Core::IFrameworkView
|
||||
{
|
||||
public:
|
||||
ViewProvider();
|
||||
virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView );
|
||||
virtual void SetWindow(Windows::UI::Core::CoreWindow^ wnd);
|
||||
virtual void Load( _In_ Platform::String^ entryPoint );
|
||||
virtual void Run();
|
||||
virtual void Uninitialize();
|
||||
|
||||
protected:
|
||||
// Event Handlers
|
||||
void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args);
|
||||
void OnResourceAvailabilityChanged(Platform::Object^ sender, Platform::Object^ args);
|
||||
void OnExiting(Platform::Object^ sender, Platform::Object^ args);
|
||||
void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args);
|
||||
void OnResuming(Platform::Object^ sender, Platform::Object^ args);
|
||||
void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args);
|
||||
|
||||
private:
|
||||
XboxPlatform* plt;
|
||||
};
|
||||
|
||||
ref class ViewProviderFactory sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
|
||||
{
|
||||
public:
|
||||
ViewProviderFactory() {}
|
||||
virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView() { return ref new ViewProvider; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////
|
||||
ViewProvider::ViewProvider()
|
||||
{
|
||||
}
|
||||
|
||||
void ViewProvider::Initialize(CoreApplicationView^ applicationView)
|
||||
{
|
||||
applicationView->Activated += ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &ViewProvider::OnActivated);
|
||||
CoreApplication::ResourceAvailabilityChanged += ref new EventHandler<Platform::Object^>(this, &ViewProvider::OnResourceAvailabilityChanged);
|
||||
CoreApplication::Exiting += ref new EventHandler<Platform::Object^>(this, &ViewProvider::OnExiting);
|
||||
CoreApplication::Suspending += ref new EventHandler<SuspendingEventArgs^>(this, &ViewProvider::OnSuspending);
|
||||
CoreApplication::Resuming += ref new EventHandler<Platform::Object^>(this, &ViewProvider::OnResuming);
|
||||
}
|
||||
|
||||
void ViewProvider::SetWindow(Windows::UI::Core::CoreWindow^ wnd)
|
||||
{
|
||||
plt = new XboxPlatform();
|
||||
}
|
||||
|
||||
void ViewProvider::Run()
|
||||
{
|
||||
RBX::StandardOut::singleton()->messageOut.connect( [](const RBX::StandardOutMessage& m) -> void { dprintf( "%s\n", m.message.c_str() ); } );
|
||||
|
||||
while (true)
|
||||
{
|
||||
Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents( Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent );
|
||||
plt->tick();
|
||||
}
|
||||
}
|
||||
|
||||
// The purpose of this method is to get the application entry point.
|
||||
void ViewProvider::Load(Platform::String^ entryPoint)
|
||||
{
|
||||
}
|
||||
|
||||
void ViewProvider::Uninitialize()
|
||||
{
|
||||
delete plt;
|
||||
}
|
||||
|
||||
// Called when the application is activated.
|
||||
void ViewProvider::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
|
||||
{
|
||||
if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol)
|
||||
{
|
||||
// Tell the game object we had a protocol activation so it can setup state appropriately.
|
||||
plt->onProtocolActivated(
|
||||
static_cast<IProtocolActivatedEventArgs^>(args)
|
||||
);
|
||||
}
|
||||
//CoreWindow::GetForCurrentThread()->Activate();
|
||||
}
|
||||
|
||||
void ViewProvider::OnExiting(Platform::Object^ sender, Platform::Object^ args)
|
||||
{
|
||||
RBX::GlobalBasicSettings::singleton()->saveState();
|
||||
RBX::GlobalAdvancedSettings::singleton()->saveState();
|
||||
}
|
||||
|
||||
void ViewProvider::OnResourceAvailabilityChanged(Platform::Object^ sender, Platform::Object^ args)
|
||||
{
|
||||
// check to see if going into constrained or leaving constrained
|
||||
if(CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Constrained)
|
||||
{
|
||||
dprintf("entering constrained state\n");
|
||||
plt->xbEventPlayerSessionPause();
|
||||
plt->stopEventHandlers();
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf("leaving constrained state\n");
|
||||
if(!plt->xboxLiveContext) return;
|
||||
|
||||
plt->startEventHandlers();
|
||||
plt->xbEventPlayerSessionResume();
|
||||
// leaving constrained state for running
|
||||
// check for inventory
|
||||
async(plt->xboxLiveContext->InventoryService->GetInventoryItemsAsync( Microsoft::Xbox::Services::Marketplace::MediaItemType::GameConsumable )).complete(
|
||||
[this](Marketplace::InventoryItemsResult^ inventoryResult){
|
||||
try
|
||||
{
|
||||
// Because we may be compiling a list of multiple content types from separate
|
||||
// calls, we just append the results to the passed in vector.
|
||||
if(inventoryResult->Items->Size > 0)
|
||||
{
|
||||
for(int i = 0; i < inventoryResult->Items->Size;i++)
|
||||
{
|
||||
if(inventoryResult->Items->GetAt(i)->ConsumableBalance > 0)
|
||||
{
|
||||
plt->sendConsumeAllRequest(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Platform::Exception^ ex)
|
||||
{
|
||||
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Called when the application is suspending.
|
||||
void ViewProvider::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
|
||||
{
|
||||
dprintf("Suspending...\n");
|
||||
try
|
||||
{
|
||||
plt->xbEventPlayerSessionEnd();
|
||||
|
||||
// this needs to be the last thing we do on suspend
|
||||
plt->suspendViewXbox();
|
||||
}
|
||||
catch(Platform::Exception ^ex)
|
||||
{
|
||||
dprintf("Suspending view failed 0x%x\n", ex->HResult);
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the application is resuming from suspended.
|
||||
void ViewProvider::OnResuming(Platform::Object^ sender, Platform::Object^ args)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this needs to be the first thing we do on resume
|
||||
plt->resumeViewXbox();
|
||||
|
||||
// Note: that resume changes the suspend state to constrained state
|
||||
// then changes the constrained state to running state
|
||||
dprintf("Resuming...\n");
|
||||
|
||||
if( plt->currentUser && plt->xboxLiveContext && plt->currentUser->XboxUserId)
|
||||
{
|
||||
//current user is signed in
|
||||
auto users = Windows::Xbox::System::User::Users;
|
||||
|
||||
for(int i = 0; i < users->Size; i++)
|
||||
{
|
||||
if(users->GetAt(i) == plt->currentUser)
|
||||
{
|
||||
if(users->GetAt(i)->IsSignedIn)
|
||||
{
|
||||
if( isStringEqual(users->GetAt(i)->XboxUserId, Windows::Xbox::ApplicationModel::Core::CoreApplicationContext::CurrentUser->XboxUserId))
|
||||
{
|
||||
dprintf("resuming for returning player\n");
|
||||
|
||||
//checking if user's controller has changed
|
||||
for(auto controller : Windows::Xbox::ApplicationModel::Core::CoreApplicationContext::CurrentUser->Controllers)
|
||||
{
|
||||
if (controller == plt->currentController)
|
||||
{
|
||||
// normal resume
|
||||
plt->xbEventPlayerSessionStart();
|
||||
plt->onNormalResume();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
plt->returnToEngagementScreen(RBX::ReturnToEngage_ControllerChange);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if the current user is no longer signed in, what then?
|
||||
dprintf("resuming for absense of player\n");
|
||||
plt->returnToEngagementScreen(RBX::ReturnToEngage_SignOut);
|
||||
}
|
||||
}
|
||||
catch(Platform::Exception ^ex)
|
||||
{
|
||||
dprintf("Resuming view failed 0x%x\n", ex->HResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Platform::MTAThread]
|
||||
int main(Platform::Array< Platform::String^ >^)
|
||||
{
|
||||
// !!! Do not remove the following line, this makes sure mmdevapi.dll is linked
|
||||
SetWasapiThreadAffinityMask(SetWasapiThreadAffinityMask(1));
|
||||
Windows::ApplicationModel::Core::CoreApplication::Run( ref new ViewProviderFactory() );
|
||||
}
|
||||
|
||||
|
||||
extern int isRetail()
|
||||
{
|
||||
#ifdef _NOOPT
|
||||
return 0;
|
||||
#else
|
||||
// let's hope the docs and the xbox dev forums are correct on this one
|
||||
// that the d:\ partition is "not available" on retail consoles
|
||||
static int x = 0 != _access("d:\\", 0);
|
||||
return x || FFlag::ForceRetail; // fflag is just in case if the d:\ drive is not enough, but the flag won't come in until the intro is initialized, so there might be some debug output happening
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
extern void dprintf( const char* fmt, ... )
|
||||
{
|
||||
if( isRetail() ) return;
|
||||
|
||||
va_list va;
|
||||
va_start( va, fmt );
|
||||
char buffer[4096];
|
||||
vsnprintf( buffer, sizeof(buffer), fmt, va );
|
||||
va_end(va);
|
||||
OutputDebugStringA(buffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
|
||||
#include "marshaller.h"
|
||||
|
||||
#include <xdk.h>
|
||||
#include <windows.h>
|
||||
#include <synchapi.h>
|
||||
|
||||
#include "rbx/Debug.h"
|
||||
#include "rbx/Profiler.h"
|
||||
|
||||
__declspec(thread) static void* g_syncEvent; // per thread
|
||||
extern void dprintf( const char* fmt, ... );
|
||||
|
||||
void* Marshaller::getSyncEvent()
|
||||
{
|
||||
if( !g_syncEvent )
|
||||
{
|
||||
g_syncEvent = CreateEvent(0, 0, 0, 0);
|
||||
RBX::mutex::scoped_lock lck(cleanupMutex);
|
||||
cleanup.push_back(&g_syncEvent);
|
||||
}
|
||||
return g_syncEvent;
|
||||
}
|
||||
|
||||
|
||||
Marshaller::Marshaller()
|
||||
{
|
||||
mainThread = GetCurrentThreadId();
|
||||
hmainThread = OpenThread(THREAD_ALL_ACCESS, FALSE, mainThread);
|
||||
RBXASSERT(hmainThread);
|
||||
|
||||
dprintf( "Marshaller started on thread %I64x (%I64d)\n", mainThread, mainThread );
|
||||
}
|
||||
|
||||
Marshaller::~Marshaller()
|
||||
{
|
||||
CloseHandle(hmainThread);
|
||||
for( int j=0, e=(int)cleanup.size(); j<e; ++j )
|
||||
{
|
||||
CloseHandle(*cleanup[j]);
|
||||
*cleanup[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Marshaller::submit(Func job)
|
||||
{
|
||||
JobDesc desc = JobDesc();
|
||||
desc.job = job;
|
||||
desc.result = 0;
|
||||
RBX::mutex::scoped_lock lock (jqMutex);
|
||||
jobQueue.push_back(desc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Marshaller::execute(Func job)
|
||||
{
|
||||
if( GetCurrentThreadId() == mainThread )
|
||||
{
|
||||
job();
|
||||
return;
|
||||
}
|
||||
|
||||
void* syncEvent = getSyncEvent();
|
||||
ResetEvent(syncEvent); // just in case
|
||||
|
||||
JobResult result = {};
|
||||
result.sync = syncEvent;
|
||||
|
||||
JobDesc desc = JobDesc();
|
||||
desc.job = job;
|
||||
desc.result = &result;
|
||||
if(1)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock (jqMutex);
|
||||
jobQueue.push_back(desc);
|
||||
}
|
||||
|
||||
WaitForSingleObject( syncEvent, INFINITE ); // wait for the result
|
||||
|
||||
if (!result.success)
|
||||
{
|
||||
throw RBX::runtime_error( "Marshaller cross-thread exception: '%s'", result.except.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void Marshaller::waitEvents()
|
||||
{
|
||||
RBXPROFILER_SCOPE("xbox", __FUNCTION__);/*
|
||||
for( int j=0; j<20; ++j )
|
||||
{
|
||||
if( runJob() ) break;
|
||||
SleepEx(100, TRUE);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Marshaller::processEvents()
|
||||
{
|
||||
RBXPROFILER_SCOPE("xbox", __FUNCTION__);
|
||||
while( runJob() ) {}
|
||||
}
|
||||
|
||||
bool Marshaller::runJob()
|
||||
{
|
||||
JobDesc desc;
|
||||
if(1)
|
||||
{
|
||||
RBX::mutex::scoped_lock lock (jqMutex);
|
||||
if(jobQueue.empty()) return false;
|
||||
desc = jobQueue.front();
|
||||
jobQueue.erase( jobQueue.begin() );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
desc.job();
|
||||
if (desc.result)
|
||||
desc.result->success = true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (desc.result)
|
||||
{
|
||||
desc.result->success = false;
|
||||
desc.result->except = e.what();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've got nothing to report to, just dump it somewhere
|
||||
// TODO: replace with debugging stuff
|
||||
dprintf("%s\n", e.what());
|
||||
RBXASSERT(0); // haha, exceptions suck
|
||||
}
|
||||
}
|
||||
|
||||
if (desc.result && desc.result->sync)
|
||||
{
|
||||
SetEvent(desc.result->sync);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "rbx/threadsafe.h"
|
||||
|
||||
|
||||
class Marshaller
|
||||
{
|
||||
typedef boost::function<void()> Func;
|
||||
|
||||
public:
|
||||
|
||||
Marshaller();
|
||||
~Marshaller();
|
||||
|
||||
// Executes the given function.
|
||||
void execute(Func job);
|
||||
|
||||
// Submits a function to be executed by a separate thread.
|
||||
void submit(Func job);
|
||||
|
||||
// blocks until something interesting happens
|
||||
void waitEvents();
|
||||
|
||||
// processes all pending callbacks
|
||||
void processEvents();
|
||||
|
||||
// returns marshaller thread
|
||||
DWORD getThreadId() const { return mainThread; }
|
||||
|
||||
private:
|
||||
struct JobResult
|
||||
{
|
||||
bool success;
|
||||
std::string except;
|
||||
void* sync;
|
||||
};
|
||||
|
||||
struct JobDesc
|
||||
{
|
||||
Func job;
|
||||
JobResult* volatile result;
|
||||
};
|
||||
|
||||
unsigned mainThread; // not the 'main' main thread
|
||||
void* hmainThread; // handle to the main thread
|
||||
|
||||
RBX::mutex jqMutex;
|
||||
std::vector< JobDesc > jobQueue;
|
||||
RBX::mutex cleanupMutex;
|
||||
std::vector< void** > cleanup;
|
||||
|
||||
void* getSyncEvent();
|
||||
bool runJob();
|
||||
};
|
||||
|
||||
extern void main_processWinRTEvents(); // used to call into WinRT crap, defined in main.cpp
|
||||
@@ -0,0 +1,600 @@
|
||||
|
||||
#include "p2p.h"
|
||||
|
||||
#include <collection.h>
|
||||
|
||||
#include "rbx/Debug.h"
|
||||
#include "async.h"
|
||||
|
||||
extern void dprintf( const char* fmt, ... );
|
||||
|
||||
enum { dbg = 0 };
|
||||
|
||||
//#undef RBXASSERT
|
||||
//#define RBXASSERT(X) ( (void)( (X) || (__debugbreak(), 1) ) )
|
||||
|
||||
|
||||
|
||||
namespace Xp2p
|
||||
{
|
||||
const wchar_t* kChatTemplateName = L"MultiplayerUdp"; // check appmanifest
|
||||
|
||||
enum
|
||||
{
|
||||
kRetransmitMaxPackets = 3,
|
||||
kRetransmitPeriodMs = 1000,
|
||||
kRetransmitLoopMs = 30,
|
||||
kRetransmitQueueLength = 1000,
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Address::Address()
|
||||
{
|
||||
memset( &rawdata, 0, sizeof(rawdata) );
|
||||
}
|
||||
|
||||
|
||||
Address::Address(in6_addr addr, unsigned port)
|
||||
{
|
||||
memset( &rawdata, 0, sizeof(rawdata) );
|
||||
sockaddr_in6& sa = (sockaddr_in6&)rawdata;
|
||||
sa.sin6_family = AF_INET6;
|
||||
sa.sin6_port = htons(port);
|
||||
sa.sin6_addr = addr;
|
||||
}
|
||||
|
||||
|
||||
char* Address::str(char* to ) const
|
||||
{
|
||||
char buf[128];
|
||||
inet_ntop(AF_INET6, &addr6(), buf, 128 );
|
||||
sprintf( to, "%s:%u", buf, port() );
|
||||
return to;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Sock::Sock(unsigned port)
|
||||
{
|
||||
int result;
|
||||
|
||||
fd = socket( AF_INET6, SOCK_DGRAM, IPPROTO_UDP );
|
||||
|
||||
int v6only = 0;
|
||||
setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6only, sizeof(v6only) ); // MS tells to do this, idk.
|
||||
|
||||
Address local( in6addr_any, port );
|
||||
result = bind( fd, local.toSockaddr(), local.size() );
|
||||
if(dbg) dprintf( "p2p: socket %d bind to local %d\n", fd, result );
|
||||
}
|
||||
|
||||
|
||||
Sock::~Sock()
|
||||
{
|
||||
closesocket( fd );
|
||||
if(dbg) dprintf( "p2p: socket %d closed\n", fd );
|
||||
}
|
||||
|
||||
|
||||
int Sock::send( const Address& to, const void* data, unsigned size, unsigned timeoutMs )
|
||||
{
|
||||
RBXASSERT(data);
|
||||
|
||||
fd_set set;
|
||||
int result;
|
||||
timeval tout = { timeoutMs / 1000, (timeoutMs % 1000)*1000 };
|
||||
|
||||
FD_ZERO( &set );
|
||||
FD_SET( fd, &set );
|
||||
result = select( 1, 0, &set, 0, &tout );
|
||||
if( result > 0 && FD_ISSET( fd, &set ) )
|
||||
{
|
||||
result = ::sendto( fd, (const char*)data, size, 0, to.toSockaddr(), to.size() );
|
||||
DWORD err = WSAGetLastError();
|
||||
return result;
|
||||
}
|
||||
if( result == 0 )
|
||||
{
|
||||
return Net_Timeout;
|
||||
}
|
||||
return Net_Fail;
|
||||
}
|
||||
|
||||
|
||||
int Sock::recv( Address* from, void* data, unsigned size, unsigned timeoutMs )
|
||||
{
|
||||
RBXASSERT(from);
|
||||
RBXASSERT(data);
|
||||
|
||||
fd_set set;
|
||||
int result;
|
||||
int namelen = from->size();
|
||||
timeval tout = { timeoutMs / 1000, (timeoutMs % 1000)*1000 };
|
||||
|
||||
FD_ZERO( &set );
|
||||
FD_SET( fd, &set );
|
||||
result = select( 1, &set, 0, 0, &tout );
|
||||
if( result > 0 && FD_ISSET( fd, &set ) )
|
||||
{
|
||||
result = ::recvfrom( fd, (char*)data, size, 0, from->toSockaddr(), &namelen );
|
||||
DWORD err = WSAGetLastError();
|
||||
return result;
|
||||
}
|
||||
if( result == 0 )
|
||||
{
|
||||
return Net_Timeout;
|
||||
}
|
||||
return Net_Fail;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
Peer::Peer()
|
||||
{
|
||||
state = State_New;
|
||||
pingTime = 0;
|
||||
userData = 0;
|
||||
devAssoc = nullptr;
|
||||
userObject = nullptr;
|
||||
txseq = 0;
|
||||
rxseq = 0;
|
||||
txseqR = 0;
|
||||
rxseqR = 0;
|
||||
rxackR = 0;
|
||||
id = 0xffffffff;
|
||||
}
|
||||
|
||||
|
||||
int Peer::setstate( int expect, int replace )
|
||||
{
|
||||
return InterlockedCompareExchange( &state, replace, expect );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void dbgPrintPacket( const PacketHeader* header )
|
||||
{
|
||||
dprintf( "packet: id %u type %u size %u peer %u seq %u seqR %u flags 0x%x\n", (unsigned)header->id, (unsigned)header->type, (unsigned)header->size, (unsigned)header->id, (unsigned)header->seq, (unsigned)header->seqR, (unsigned)header->flags );
|
||||
}
|
||||
|
||||
static void dbgPrintTx( PeerPtr peer )
|
||||
{
|
||||
dprintf( "peer: id %u tx %u rx %u txr %u rxr %u\n", (unsigned)peer->id, (unsigned)peer->txseq, (unsigned)peer->rxseq, (unsigned)peer->txseqR, (unsigned)peer->rxseqR );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Network::Network( MultiplayerSessionMember^ currentUser )
|
||||
: sock( kNetworkPort )
|
||||
{
|
||||
myMemberId = currentUser->MemberId;
|
||||
assTemplate = SecureDeviceAssociationTemplate::GetTemplateByName( ref new Platform::String(kChatTemplateName) );
|
||||
controlRunFlag = 1;
|
||||
controlThreadObject = boost::thread( [this]()->void { controlThread(); } );
|
||||
|
||||
incomingHandler = assTemplate->AssociationIncoming += ref new TypedEventHandler<SecureDeviceAssociationTemplate^, SecureDeviceAssociationIncomingEventArgs^>(
|
||||
[this]( SecureDeviceAssociationTemplate^, SecureDeviceAssociationIncomingEventArgs^ args )->void
|
||||
{
|
||||
lock lck(peerMutex);
|
||||
pendingList.push_back( args->Association );
|
||||
if(dbg) dprintf( "p2p: incoming association: %S\n", args->Association->RemoteHostName->DisplayName->Data() );
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Network::~Network()
|
||||
{
|
||||
controlRunFlag = 0;
|
||||
controlThreadObject.join();
|
||||
|
||||
{
|
||||
lock lck(peerMutex);
|
||||
for( auto p: peers )
|
||||
destroyPeer(p);
|
||||
}
|
||||
|
||||
for( unsigned num = 1; num; )
|
||||
{
|
||||
{
|
||||
lock lck(peerMutex);
|
||||
num = peers.size();
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
assTemplate->AssociationIncoming -= incomingHandler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
NetResult Network::sendPacket( PeerPtr peer, PacketHeader* packet, unsigned timeoutMs, unsigned flags )
|
||||
{
|
||||
RBXASSERT( packet->type != 0 ); // don't use type 0, probably an error!
|
||||
RBXASSERT( packet->size <= sizeof(PacketStorage) ); // uwot!
|
||||
RBXASSERT( packet->size >= sizeof(PacketHeader) ); // uwot! #2
|
||||
RBXASSERT( peer.get() );
|
||||
|
||||
int oldstate = peer->setstate( Peer::State_Ready, Peer::State_Busy );
|
||||
switch(oldstate)
|
||||
{
|
||||
case Peer::State_Ready:
|
||||
break;
|
||||
case Peer::State_Busy:
|
||||
case Peer::State_Busy2:
|
||||
return Net_Retry;
|
||||
default:
|
||||
return Net_NotReady;
|
||||
}
|
||||
|
||||
packet->id = myMemberId;
|
||||
packet->flags = flags;
|
||||
packet->seq = ++ peer->txseq;
|
||||
packet->timestamp = GetTickCount();
|
||||
|
||||
if( flags & Packet_Reliable )
|
||||
{
|
||||
packet->seqR = ++ peer->txseqR;
|
||||
|
||||
if( peer->reliable.size() < kRetransmitQueueLength )
|
||||
{
|
||||
PacketHeader* copy = (PacketHeader*)malloc( packet->size ); // retain the packet
|
||||
memcpy( copy, packet, packet->size );
|
||||
peer->reliable.push_back( copy );
|
||||
}
|
||||
else
|
||||
{
|
||||
dprintf( "p2p: WARN: peer retransmit queue is FULL, packet will not be reliable: ");
|
||||
dbgPrintPacket(packet);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
packet->seqR = peer->txseqR;
|
||||
}
|
||||
// NOTE: do not change the packet below! If it's reliable, its copy will not have the changes!
|
||||
|
||||
if( Peer::State_Busy != peer->setstate( Peer::State_Busy, Peer::State_Ready ) )
|
||||
RBXASSERT( !"peer lock violation" );
|
||||
|
||||
char buf[128];
|
||||
if(dbg) dprintf( "p2p: outgoing to %s (%u) type %u seq %u seqR %u size %u flags 0x%x", peer->remoteAddr.str(buf), (unsigned)packet->id, (unsigned)packet->type, (unsigned)packet->seq, (unsigned)packet->seqR, (unsigned)packet->size, (unsigned)packet->flags );
|
||||
|
||||
// assumes peer->remoteAddr never changes
|
||||
int result = sock.send( peer->remoteAddr, packet, packet->size, timeoutMs );
|
||||
|
||||
if(dbg) dprintf( result == packet->size? "... ok\n": "... failed!\n" );
|
||||
|
||||
if( result < 0 )
|
||||
{
|
||||
return (NetResult)result;
|
||||
}
|
||||
if( result != packet->size )
|
||||
{
|
||||
return Net_Fail;
|
||||
}
|
||||
return Net_Ok;
|
||||
}
|
||||
|
||||
|
||||
NetResult Network::recvPacket( PacketStorage* packet, unsigned timeoutMs )
|
||||
{
|
||||
Address from;
|
||||
int result = sock.recv( &from, packet, sizeof(PacketStorage), timeoutMs );
|
||||
|
||||
if( result < 0 ) return (NetResult)result; // recv error (timeout/disconnect/whatever)
|
||||
if( packet->size > sizeof(PacketStorage) ) return Net_Packet; // bad packet size
|
||||
if( (unsigned)result < sizeof(PacketHeader) ) return Net_Packet; // bad packet size
|
||||
|
||||
char buf[128];
|
||||
if(dbg) dprintf("p2p: incoming from %s (%u) type %u seq %u seqR %u size %u flags 0x%x ... ok\n", from.str(buf), (unsigned)packet->id, (unsigned)packet->type, (unsigned)packet->seq, (unsigned)packet->seqR, (unsigned)packet->size, (unsigned)packet->flags );
|
||||
return Net_Ok;
|
||||
}
|
||||
|
||||
// it's kinda out-of-band, sequencing is irrelevant
|
||||
static bool sendAck( Sock& sock, const Address& addr, unsigned myid, unsigned seqR )
|
||||
{
|
||||
PacketHeader ack = {};
|
||||
ack.type = 0; // yep
|
||||
ack.seqR = seqR;
|
||||
ack.id = myid;
|
||||
ack.timestamp = GetTickCount();
|
||||
|
||||
int acqr = sock.send( addr, &ack, sizeof(ack), 10 );
|
||||
return acqr == sizeof(ack);
|
||||
}
|
||||
|
||||
NetResult Network::processPacket( PeerPtr& who, PacketHeader* header )
|
||||
{
|
||||
char buf[120];
|
||||
|
||||
if(1)
|
||||
{
|
||||
lock lck(peerMutex);
|
||||
for( auto& itor: peers ) if( itor->id == header->id ) { who = itor; break; }
|
||||
}
|
||||
|
||||
if( !who ) return Net_Peer;
|
||||
|
||||
int oldstate = who->setstate( Peer::State_Ready, Peer::State_Busy );
|
||||
switch(oldstate)
|
||||
{
|
||||
case Peer::State_Ready:
|
||||
break;
|
||||
case Peer::State_Busy:
|
||||
case Peer::State_Busy2:
|
||||
return Net_Retry;
|
||||
default:
|
||||
return Net_NotReady;
|
||||
}
|
||||
|
||||
Address remoteAddr( who->remoteAddr );
|
||||
bool isReliable = header->flags & Packet_Reliable;
|
||||
bool needAck = false;
|
||||
NetResult result; // intentionally uninitialized
|
||||
|
||||
if( header->type == 0 ) // it's an ack packet from the peer
|
||||
{
|
||||
if( header->seqR == who->rxackR + 1 )
|
||||
{
|
||||
if(dbg) dprintf("p2p: got ACK for %u\n", header->seqR );
|
||||
who->rxackR = header->seqR;
|
||||
}
|
||||
result = Net_Packet; // the client is not interested in it, discard
|
||||
}
|
||||
else if( isReliable )
|
||||
{
|
||||
if( header->seqR == who->rxseqR + 1 ) // okay: although the reliable packet is what we expect, looks like we're going to lose some unreliable packets later
|
||||
{
|
||||
needAck = true;
|
||||
result = Net_Ok;
|
||||
}
|
||||
else if( header->seqR <= who->rxseqR ) // okay..ish: it's from the past, so we discard the packet but send an ack
|
||||
{
|
||||
needAck = true;
|
||||
result = Net_Packet;
|
||||
}
|
||||
else // it's from the future, so discard, because there's another reliable packet in flight
|
||||
{
|
||||
result = Net_Packet;
|
||||
}
|
||||
}
|
||||
else // regular
|
||||
{
|
||||
if( header->seq >= who->rxseq + 1 ) // packet is in-sequence or from the future
|
||||
{
|
||||
if( header->seqR == who->rxseqR ) // good: we don't have any reliable packets in-between, so even if the packet is from the future, we still accept it
|
||||
{
|
||||
result = Net_Ok;
|
||||
}
|
||||
else // bad: there's a reliable packet with a lower 'seq' that's not been delivered yet, discard this packet
|
||||
{
|
||||
result = Net_Packet;
|
||||
}
|
||||
}
|
||||
else // the packet is from the past, discard
|
||||
{
|
||||
result = Net_Packet;
|
||||
}
|
||||
}
|
||||
|
||||
// final step:
|
||||
if( result == Net_Ok ) // update on success
|
||||
{
|
||||
who->rxseq = header->seq;
|
||||
who->rxseqR = header->seqR;
|
||||
who->pingTime = GetTickCount();
|
||||
}
|
||||
|
||||
who->setstate( Peer::State_Busy, Peer::State_Ready );
|
||||
|
||||
if( needAck )
|
||||
{
|
||||
sendAck(sock, remoteAddr, myMemberId, header->seqR);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PeerPtr Network::createPeer( MultiplayerSessionMember^ member, boost::function<void(PeerPtr)> onConnected )
|
||||
{
|
||||
lock lck(peerMutex);
|
||||
|
||||
for( auto x: peers )
|
||||
if( x->id == member->MemberId )
|
||||
return x;
|
||||
|
||||
|
||||
PeerPtr peer( new Peer );
|
||||
peers.push_back(peer);
|
||||
boost::thread( [this, peer, member, onConnected]() -> void { connectPeer( peer, member, onConnected ); } ).detach();
|
||||
return peer;
|
||||
}
|
||||
|
||||
|
||||
void Network::destroyPeer( PeerPtr peer )
|
||||
{
|
||||
RBXASSERT( !peers.empty() ); // um...
|
||||
boost::thread(
|
||||
[this, peer]() -> void
|
||||
{
|
||||
unsigned index;
|
||||
for (;;)
|
||||
{
|
||||
peerMutex.lock();
|
||||
index = std::find( peers.begin(), peers.end(), peer ) - peers.begin();
|
||||
if( index == peers.size() ) { peerMutex.unlock(); return; } // not found
|
||||
|
||||
if( Peer::State_Ready == peer->setstate( Peer::State_Ready, Peer::State_Dead ) ) break;
|
||||
if( Peer::State_New == peer->setstate( Peer::State_New, Peer::State_Dead ) ) break;
|
||||
if( Peer::State_Waiting == peer->setstate( Peer::State_Waiting, Peer::State_Dead ) ) break;
|
||||
if( Peer::State_Error == peer->setstate( Peer::State_Error, Peer::State_Dead ) ) break;
|
||||
peerMutex.unlock();
|
||||
|
||||
SleepEx(10, false);
|
||||
}
|
||||
// the mutex is still locked
|
||||
|
||||
char buf[128];
|
||||
if(dbg) dprintf( "p2p: destroy peer %s\n", peer->remoteAddr.str(buf) );
|
||||
|
||||
for( auto ptr: peer->reliable ) { free(ptr); }
|
||||
|
||||
RBXASSERT( !peers.empty() );
|
||||
RBXASSERT( index < peers.size() );
|
||||
peers[index] = peers.back();
|
||||
peers.resize( peers.size() - 1 );
|
||||
|
||||
async( peer->devAssoc->DestroyAsync() ).detach();
|
||||
peer->devAssoc = nullptr;
|
||||
|
||||
peerMutex.unlock();
|
||||
}
|
||||
).detach();
|
||||
}
|
||||
|
||||
|
||||
void Network::connectPeer( PeerPtr peer, MultiplayerSessionMember^ member, boost::function<void(PeerPtr)> onConnected )
|
||||
{
|
||||
if( Peer::State_New != peer->setstate( Peer::State_New, Peer::State_Connecting ) )
|
||||
RBXASSERT( !"oops" );
|
||||
|
||||
SecureDeviceAddress^ addr = SecureDeviceAddress::FromBase64String( member->SecureDeviceAddressBase64 );
|
||||
auto devAssTemplate = this->assTemplate;
|
||||
|
||||
try
|
||||
{
|
||||
async( devAssTemplate->CreateAssociationAsync( addr, CreateSecureDeviceAssociationBehavior::Default ) )
|
||||
.complete(
|
||||
[peer]( SecureDeviceAssociation^ d ) mutable
|
||||
{
|
||||
peer->devAssoc = d;
|
||||
}
|
||||
).join();
|
||||
}
|
||||
catch(Platform::Exception^ e )
|
||||
{
|
||||
// maybe try again later?
|
||||
peer->setstate( Peer::State_Connecting, Peer::State_Error );
|
||||
return;
|
||||
}
|
||||
|
||||
while(!peer->devAssoc)
|
||||
{
|
||||
if(1)
|
||||
{
|
||||
lock lck(peerMutex);
|
||||
for( unsigned j = 0; j<pendingList.size(); ++j )
|
||||
{
|
||||
auto pendingAssoc = pendingList[j];
|
||||
SecureDeviceAddress^ pendingAddr = pendingAssoc->RemoteSecureDeviceAddress;
|
||||
if( 0 == addr->Compare(pendingAddr) )
|
||||
{
|
||||
peer->devAssoc = pendingAssoc;
|
||||
pendingList[j] = pendingList.back();
|
||||
pendingList.resize( pendingList.size() - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(peer->devAssoc) break;
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
if(dbg) dprintf( "p2p: connected to: %S\n", peer->devAssoc->RemoteHostName->DisplayName->Data() );
|
||||
|
||||
peer->id = member->MemberId;
|
||||
Platform::ArrayReference<BYTE> remoteSocketAddressBytes( (BYTE*)&peer->remoteAddr.rawdata, sizeof(peer->remoteAddr.rawdata) );
|
||||
peer->devAssoc->GetRemoteSocketAddressBytes( remoteSocketAddressBytes );
|
||||
|
||||
// got the address, we can now send the data
|
||||
peer->setstate( Peer::State_Connecting, Peer::State_Ready );
|
||||
onConnected(peer);
|
||||
}
|
||||
|
||||
|
||||
// transmission control
|
||||
void Network::controlThread()
|
||||
{
|
||||
unsigned peerItor = 0;
|
||||
while( controlRunFlag )
|
||||
{
|
||||
Sleep(kRetransmitLoopMs);
|
||||
|
||||
// pick a peer
|
||||
PeerPtr peer;
|
||||
peerMutex.lock();
|
||||
if( !peers.empty() )
|
||||
peer = peers[ peerItor++ % peers.size() ];
|
||||
peerMutex.unlock();
|
||||
|
||||
if(!peer)
|
||||
continue;
|
||||
|
||||
// Acquire a lock on the peer
|
||||
int oldstate;
|
||||
while( 1 )
|
||||
{
|
||||
oldstate = peer->setstate( Peer::State_Ready, Peer::State_Busy2 );
|
||||
if( oldstate == Peer::State_Ready ) break; // good
|
||||
if( oldstate != Peer::State_Busy ) break; // bad
|
||||
Sleep(1); // wait a bit and try again
|
||||
}
|
||||
|
||||
if( oldstate != Peer::State_Ready )
|
||||
continue; // the peer is not in an operational state, ignore
|
||||
|
||||
unsigned delUpTo = 0;
|
||||
unsigned numSend = 0;
|
||||
PacketHeader* toSend[kRetransmitMaxPackets] = {};
|
||||
|
||||
for( unsigned j=0, e=peer->reliable.size(); j<e && numSend < kRetransmitMaxPackets; ++j )
|
||||
{
|
||||
PacketHeader* packet = peer->reliable[j];
|
||||
if( packet->seqR <= peer->rxackR )
|
||||
{
|
||||
delUpTo = j; // we got confirmation for this packet, discard
|
||||
continue;
|
||||
}
|
||||
|
||||
if( packet->timestamp + kRetransmitPeriodMs > GetTickCount() )
|
||||
break; // the packet is not yet eligible for retry, and all following packets either, because they're ordered
|
||||
|
||||
toSend[numSend++] = packet;
|
||||
}
|
||||
|
||||
if( delUpTo )
|
||||
{
|
||||
for( unsigned j=0; j<delUpTo; ++j )
|
||||
{
|
||||
if(dbg) { dprintf("p2p: removing reliable packet: "); dbgPrintPacket(peer->reliable[j] ); }
|
||||
free(peer->reliable[j]);
|
||||
}
|
||||
peer->reliable.erase( peer->reliable.begin(), peer->reliable.begin() + delUpTo );
|
||||
}
|
||||
|
||||
// unlock the peer, as the actual net send operations can be concurrent
|
||||
if( Peer::State_Busy2 != peer->setstate(Peer::State_Busy2, Peer::State_Ready ) )
|
||||
RBXASSERT( !"peer lock violation #2" );
|
||||
|
||||
// retransmit
|
||||
for( unsigned j=0; j<numSend; ++j )
|
||||
{
|
||||
toSend[j]->timestamp = GetTickCount();
|
||||
if(dbg) { dprintf("p2p: retransmit "); dbgPrintPacket(toSend[j]); }
|
||||
int result = sock.send( peer->remoteAddr, toSend[j], toSend[j]->size, 10 );
|
||||
if( result < 0 ) break; // error with the socket don't continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Bool { True, False, IDunno };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
|
||||
// p2p.h - peer to peer UDP networking for voice
|
||||
//
|
||||
// This header is *very* toxic.
|
||||
|
||||
#include <wrl.h>
|
||||
#include <xdk.h>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <winsock2.h>
|
||||
//#include <ws2def.h>
|
||||
#include <ws2ipdef.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
|
||||
using namespace Microsoft::Xbox::Services::Multiplayer;
|
||||
using namespace Windows::Xbox::Networking;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
namespace Xp2p
|
||||
{
|
||||
struct PacketHeader;
|
||||
class Peer;
|
||||
typedef boost::shared_ptr<Peer> PeerPtr;
|
||||
|
||||
enum
|
||||
{
|
||||
kNetworkPort = 8700, // NOTE: check the manifest!
|
||||
kMaxUdpSize = 1264, // what MS says in the XDK help
|
||||
};
|
||||
|
||||
enum NetResult
|
||||
{
|
||||
Net_Ok,
|
||||
Net_Fail = -1, // operation failed
|
||||
Net_Timeout = -2, // operation timed out (you'll be getting quite a few of these)
|
||||
Net_Peer = -3, // bad peer
|
||||
Net_Retry = -4, // operation aborted because the peer was busy, the calling code should try again
|
||||
Net_NotReady = -5, // operation aborted because the peer is not ready,
|
||||
Net_Packet = -6, // bad incoming packet
|
||||
};
|
||||
|
||||
enum PacketFlags
|
||||
{
|
||||
Packet_Default = 0,
|
||||
Packet_Reliable = 1,
|
||||
Packet_Ordered = 2,
|
||||
};
|
||||
|
||||
struct Address
|
||||
{
|
||||
SOCKADDR_STORAGE rawdata;
|
||||
|
||||
Address();
|
||||
Address(in6_addr addr, unsigned port);
|
||||
|
||||
sockaddr* toSockaddr() const { return (sockaddr*)&rawdata; }
|
||||
int size() const { return sizeof(rawdata); }
|
||||
in6_addr addr6() const { return ((sockaddr_in6&)rawdata).sin6_addr; }
|
||||
unsigned port() const { return ntohs( ((sockaddr_in6&)rawdata).sin6_port ); }
|
||||
char* str(char* to) const;
|
||||
};
|
||||
|
||||
class Sock : public boost::noncopyable
|
||||
{
|
||||
int fd;
|
||||
public:
|
||||
Sock(unsigned port);
|
||||
~Sock();
|
||||
int send( const Address& to, const void* data, unsigned size, unsigned timeoutMs );
|
||||
int recv( Address* from, void* data, unsigned size, unsigned timeoutMs );
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Peer : public boost::enable_shared_from_this<Peer>, boost::noncopyable
|
||||
{
|
||||
friend class Network;
|
||||
friend void dbgPrintTx(PeerPtr p);
|
||||
|
||||
enum State { State_New, State_Connecting, State_Waiting, State_Ready, State_Busy, State_Busy2, State_Error, State_Dead };
|
||||
|
||||
volatile long state; // mutex and state indicator
|
||||
SecureDeviceAssociation^ devAssoc; // device association (kinda like VPN transport layer between two consoles)
|
||||
Address remoteAddr; // remote address as reported by devass
|
||||
uint32 pingTime; // last ping time
|
||||
uint32 id; // peer id (MultiplayerSessionMember::MemberId)
|
||||
|
||||
// transmission control, see comment below
|
||||
uint32 txseq;
|
||||
uint32 rxseq;
|
||||
uint32 txseqR;
|
||||
uint32 rxseqR;
|
||||
uint32 rxackR;
|
||||
|
||||
std::vector<PacketHeader*> reliable; // retransmit queue for reliable packets
|
||||
|
||||
Peer();
|
||||
int setstate( int expect, int replace );
|
||||
|
||||
public:
|
||||
void* userData; // whatever you want to put in here
|
||||
Platform::Object^ userObject; // or here
|
||||
~Peer() {}
|
||||
|
||||
uint32 getPeerId() const { return id; }
|
||||
};
|
||||
/*
|
||||
* TODO: write an essay
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Packets
|
||||
|
||||
#include <pshpack1.h>
|
||||
struct PacketHeader
|
||||
{
|
||||
// fill these out:
|
||||
uint16 size; // entire packet size (incl. this header)
|
||||
uint8 type; // type 0 is reserved, do not use!
|
||||
|
||||
// don't bother with these:
|
||||
uint8 flags; // a combination of Packet_XXX
|
||||
uint32 seq; // sequence # (all)
|
||||
uint32 seqR; // sequence # (last reliable)
|
||||
uint32 id; // peer id (MultiplayerSessionMember::MemberId)
|
||||
uint32 timestamp;
|
||||
|
||||
uint8 reserved[32]; // reserved for now
|
||||
};
|
||||
|
||||
|
||||
// defines a packet of maximum length
|
||||
// used to recv() an unknown packet
|
||||
struct PacketStorage: PacketHeader
|
||||
{
|
||||
enum { MaxPayloadSize = kMaxUdpSize - sizeof(PacketHeader) };
|
||||
uint8 payload[MaxPayloadSize];
|
||||
};
|
||||
#include <poppack.h>
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Network
|
||||
|
||||
|
||||
class Network: public boost::noncopyable
|
||||
{
|
||||
typedef std::lock_guard<std::mutex> lock;
|
||||
Sock sock;
|
||||
|
||||
uint32 myMemberId; // (MultiplayerSessionMember::MemberId)
|
||||
SecureDeviceAssociationTemplate^ assTemplate;
|
||||
Windows::Foundation::EventRegistrationToken incomingHandler;
|
||||
volatile long controlRunFlag; // control thread run/exit flag
|
||||
|
||||
std::mutex peerMutex; // only used to manipulate these arrays; use Peer::setstate() to lock individual peers
|
||||
std::vector< PeerPtr > peers;
|
||||
std::vector< SecureDeviceAssociation^ > pendingList;
|
||||
boost::thread controlThreadObject;
|
||||
|
||||
public:
|
||||
Network( MultiplayerSessionMember^ currentUser );
|
||||
~Network();
|
||||
|
||||
PeerPtr createPeer( MultiplayerSessionMember^ member, boost::function<void(PeerPtr)> onConnected );
|
||||
|
||||
void destroyPeer( PeerPtr peer );
|
||||
|
||||
// sends a packet to that peer
|
||||
// returns number of bytes sent or -1 on error
|
||||
NetResult sendPacket( PeerPtr peer, PacketHeader* packet, unsigned timeoutMs, unsigned flags );
|
||||
|
||||
// receives a packet
|
||||
NetResult recvPacket( PacketStorage* stor, unsigned timeoutMs );
|
||||
|
||||
// MUST be called after each recvPacket
|
||||
// figures our who sent this packet
|
||||
NetResult processPacket( PeerPtr& who, PacketHeader* header );
|
||||
|
||||
private:
|
||||
void connectPeer( PeerPtr peer, MultiplayerSessionMember^ member, boost::function<void(PeerPtr)> onConnected ); // call on a separate thread!
|
||||
PeerPtr getPeerByAssoc( SecureDeviceAssociation^ incoming );
|
||||
|
||||
|
||||
void controlThread();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
#include "renderJob.h"
|
||||
|
||||
|
||||
#include "v8datamodel/BaseRenderJob.h"
|
||||
#include "v8datamodel/DataModel.h"
|
||||
#include "rbx/rbxTime.h"
|
||||
|
||||
#include "GfxBase/ViewBase.h"
|
||||
#include "GfxBase/FrameRateManager.h"
|
||||
#include "RenderSettingsItem.h"
|
||||
|
||||
#include "marshaller.h"
|
||||
#include "rbx/Profiler.h"
|
||||
|
||||
RenderJob::RenderJob(RBX::ViewBase* v, Marshaller* m)
|
||||
: RBX::BaseRenderJob(CRenderSettingsItem::singleton().getMinFrameRate(), CRenderSettingsItem::singleton().getMaxFrameRate(), shared_from(v->getDataModel()) )
|
||||
{
|
||||
view = v;
|
||||
marshaller = m;
|
||||
stopped = 0;
|
||||
}
|
||||
|
||||
void RenderJob::stop()
|
||||
{
|
||||
stopped = 1;
|
||||
}
|
||||
|
||||
static void scheduleRenderPerform(const weak_ptr<RenderJob>& selfWeak, RBX::ViewBase* view, double timeJobStart)
|
||||
{
|
||||
if (shared_ptr<RenderJob> self = selfWeak.lock())
|
||||
{
|
||||
view->renderPerform(timeJobStart);
|
||||
self->wake();
|
||||
}
|
||||
}
|
||||
|
||||
RBX::TaskScheduler::StepResult RenderJob::stepDataModelJob(const Stats& stats)
|
||||
{
|
||||
RBXPROFILER_SCOPE("Jobs", __FUNCTION__);
|
||||
if (stopped)
|
||||
{
|
||||
return RBX::TaskScheduler::Done;
|
||||
}
|
||||
|
||||
RBX::DataModel* dm = view->getDataModel();
|
||||
RBX::FrameRateManager* frm = view->getFrameRateManager();
|
||||
double seconds = 0.016;
|
||||
|
||||
if(1)
|
||||
{
|
||||
RBX::DataModel::scoped_write_request request(dm);
|
||||
|
||||
if(frm) seconds = frm->GetFrameTimeStats().getLatest() / 1000.f;
|
||||
dm->renderStep(seconds);
|
||||
|
||||
if( frm )
|
||||
frm->configureFrameRateManager(RBX::CRenderSettings::FrameRateManagerAuto, true);
|
||||
|
||||
isAwake = false;
|
||||
|
||||
seconds = RBX::Time::nowFastSec();
|
||||
marshaller->execute( boost::bind(&RBX::ViewBase::renderPrepare, view, this) );
|
||||
}
|
||||
marshaller->submit(boost::bind(&scheduleRenderPerform, weak_from(this), view, seconds));
|
||||
return RBX::TaskScheduler::Stepped;
|
||||
}
|
||||
|
||||
|
||||
RBX::Time::Interval RenderJob::sleepTime(const Stats& stats)
|
||||
{
|
||||
if (isAwake)
|
||||
return computeStandardSleepTime(stats, maxFrameRate);
|
||||
else
|
||||
return RBX::Time::Interval::max();
|
||||
}
|
||||
|
||||
std::string RenderJob::getMetric(const std::string& metric) const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
double RenderJob::getMetricValue(const std::string& metric) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "v8datamodel/BaseRenderJob.h"
|
||||
|
||||
class Marshaller;
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class View;
|
||||
class ViewBase;
|
||||
}
|
||||
|
||||
|
||||
// This job calls ViewBase::render(), which needs to be done exclusive to the
|
||||
// DataModel. This is why it has the DataModelJob::Render enum, which
|
||||
// prevents concurrent writes to DataModel. It also needs to run in the view's
|
||||
// thread for OpenGL.
|
||||
// TODO: Can Ogre be modified to not require the thread?
|
||||
class RenderJob : public RBX::BaseRenderJob, public RBX::IMetric
|
||||
{
|
||||
Marshaller* marshaller;
|
||||
RBX::ViewBase* view;
|
||||
volatile int stopped;
|
||||
|
||||
public:
|
||||
RenderJob(RBX::ViewBase* robloxView, Marshaller* marshaller);
|
||||
|
||||
virtual RBX::TaskScheduler::StepResult stepDataModelJob(const Stats& stats);
|
||||
virtual RBX::Time::Interval RenderJob::sleepTime(const Stats& stats);
|
||||
|
||||
virtual std::string getMetric(const std::string& metric) const;
|
||||
virtual double getMetricValue(const std::string& metric) const;
|
||||
|
||||
void stop();
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"%DurangoXDK%"bin\xcetool Events-RBLX.0-1465F7BC.man -c xdpevents.h
|
||||
@@ -0,0 +1,626 @@
|
||||
|
||||
//**********************************************************************`
|
||||
//* This is an include file generated by EtwPlusTool. *`
|
||||
//* *`
|
||||
//* Copyright (c) Microsoft Corporation. All Rights Reserved. *`
|
||||
//**********************************************************************`
|
||||
#pragma once
|
||||
#pragma pack(push, 16)
|
||||
|
||||
#include "EtwPlus.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Field Descriptors, used in the ETX_EVENT_DESCRIPTOR array below
|
||||
//
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_A_DeleteMe_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AvatarsEquipped_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_Award10DayRoll_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_Award20DayRoll_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_Award3DayRoll_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardDeepDiver_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardFoursCompany_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardOneNameManyFaces_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardPollster_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardSampler_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardStrengthInNumbers_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardWorldTraveler_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_AwardYouDidIt_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_CurrentAvatar_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_GameProgress_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Float,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_GamesCount_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_MultiplayerRoundEnd_Fields[11] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Float,0},{EtxFieldType_Int32,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_MultiplayerRoundStart_Fields[9] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_PartyCount_Fields[3] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_PlayerSessionEnd_Fields[7] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_PlayerSessionPause_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_PlayerSessionResume_Fields[6] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}};
|
||||
EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR RBLX_1465F7BC_PlayerSessionStart_Fields[6] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}};
|
||||
|
||||
// Event name mapping
|
||||
//
|
||||
#define A_DeleteMe_value 1
|
||||
#define AvatarsEquipped_value 2
|
||||
#define Award10DayRoll_value 3
|
||||
#define Award20DayRoll_value 4
|
||||
#define Award3DayRoll_value 5
|
||||
#define AwardDeepDiver_value 6
|
||||
#define AwardFoursCompany_value 7
|
||||
#define AwardOneNameManyFaces_value 8
|
||||
#define AwardPollster_value 9
|
||||
#define AwardSampler_value 10
|
||||
#define AwardStrengthInNumbers_value 11
|
||||
#define AwardWorldTraveler_value 12
|
||||
#define AwardYouDidIt_value 13
|
||||
#define CurrentAvatar_value 14
|
||||
#define GameProgress_value 15
|
||||
#define GamesCount_value 16
|
||||
#define MultiplayerRoundEnd_value 17
|
||||
#define MultiplayerRoundStart_value 18
|
||||
#define PartyCount_value 19
|
||||
#define PlayerSessionEnd_value 20
|
||||
#define PlayerSessionPause_value 21
|
||||
#define PlayerSessionResume_value 22
|
||||
#define PlayerSessionStart_value 23
|
||||
|
||||
// Event Descriptor array
|
||||
//
|
||||
EXTERN_C __declspec(selectany) ETX_EVENT_DESCRIPTOR RBLX_1465F7BCEvents[23] = {
|
||||
{{ 1, 0, 0, 0, 0, 0, 0x0 }, "A_DeleteMe", "0.7.IGB-2.0", RBLX_1465F7BC_A_DeleteMe_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 2, 1, 0, 0, 0, 0, 0x0 }, "AvatarsEquipped", "0.7.IGB-2.1", RBLX_1465F7BC_AvatarsEquipped_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 3, 0, 0, 0, 0, 0, 0x0 }, "Award10DayRoll", "0.7.IGB-2.0", RBLX_1465F7BC_Award10DayRoll_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 4, 0, 0, 0, 0, 0, 0x0 }, "Award20DayRoll", "0.7.IGB-2.0", RBLX_1465F7BC_Award20DayRoll_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 5, 0, 0, 0, 0, 0, 0x0 }, "Award3DayRoll", "0.7.IGB-2.0", RBLX_1465F7BC_Award3DayRoll_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 6, 0, 0, 0, 0, 0, 0x0 }, "AwardDeepDiver", "0.7.IGB-2.0", RBLX_1465F7BC_AwardDeepDiver_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 7, 0, 0, 0, 0, 0, 0x0 }, "AwardFoursCompany", "0.7.IGB-2.0", RBLX_1465F7BC_AwardFoursCompany_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 8, 0, 0, 0, 0, 0, 0x0 }, "AwardOneNameManyFaces", "0.7.IGB-2.0", RBLX_1465F7BC_AwardOneNameManyFaces_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 9, 0, 0, 0, 0, 0, 0x0 }, "AwardPollster", "0.7.IGB-2.0", RBLX_1465F7BC_AwardPollster_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 10, 0, 0, 0, 0, 0, 0x0 }, "AwardSampler", "0.7.IGB-2.0", RBLX_1465F7BC_AwardSampler_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 11, 0, 0, 0, 0, 0, 0x0 }, "AwardStrengthInNumbers", "0.7.IGB-2.0", RBLX_1465F7BC_AwardStrengthInNumbers_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 12, 0, 0, 0, 0, 0, 0x0 }, "AwardWorldTraveler", "0.7.IGB-2.0", RBLX_1465F7BC_AwardWorldTraveler_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 13, 0, 0, 0, 0, 0, 0x0 }, "AwardYouDidIt", "0.7.IGB-2.0", RBLX_1465F7BC_AwardYouDidIt_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 14, 0, 0, 0, 0, 0, 0x0 }, "CurrentAvatar", "0.7.IGB-2.0", RBLX_1465F7BC_CurrentAvatar_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 15, 0, 0, 0, 0, 0, 0x0 }, "GameProgress", "0.7.IGGP-2.0", RBLX_1465F7BC_GameProgress_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 16, 0, 0, 0, 0, 0, 0x0 }, "GamesCount", "0.7.IGB-2.0", RBLX_1465F7BC_GamesCount_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 17, 0, 0, 0, 0, 0, 0x0 }, "MultiplayerRoundEnd", "0.7.IGMRE-2.0", RBLX_1465F7BC_MultiplayerRoundEnd_Fields, 11, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 18, 0, 0, 0, 0, 0, 0x0 }, "MultiplayerRoundStart", "0.7.IGMRS-2.0", RBLX_1465F7BC_MultiplayerRoundStart_Fields, 9, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 19, 0, 0, 0, 0, 0, 0x0 }, "PartyCount", "0.7.IGB-2.0", RBLX_1465F7BC_PartyCount_Fields, 3, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 20, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionEnd", "0.7.IGPSE-2.0", RBLX_1465F7BC_PlayerSessionEnd_Fields, 7, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 21, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionPause", "0.7.IGPSPA-2.0", RBLX_1465F7BC_PlayerSessionPause_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 22, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionResume", "0.7.IGPSR-2.0", RBLX_1465F7BC_PlayerSessionResume_Fields, 6, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault },
|
||||
{{ 23, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionStart", "0.7.IGPSS-2.0", RBLX_1465F7BC_PlayerSessionStart_Fields, 6, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }};
|
||||
|
||||
// Provider Descriptor for RBLX_1465F7BC
|
||||
//
|
||||
EXTERN_C __declspec(selectany) ETX_PROVIDER_DESCRIPTOR RBLX_1465F7BCProvider = {"RBLX_1465F7BC", {0xc01cb476,0x5b6e,0x430d,{0xa9,0x07,0xcd,0x42,0x2e,0xfb,0x76,0x4a}}, 23, (ETX_EVENT_DESCRIPTOR*)&RBLX_1465F7BCEvents, 0, EtxProviderEnabledState_Undefined, EtxProviderEnabledState_OnByDefault, 0, 100, EtxProviderLatency_Undefined, EtxProviderLatency_RealTime, EtxProviderPriority_Undefined, EtxProviderPriority_Critical};
|
||||
|
||||
// ETW handle for RBLX_1465F7BC
|
||||
//
|
||||
EXTERN_C __declspec(selectany) REGHANDLE RBLX_1465F7BCHandle = (REGHANDLE)0;
|
||||
|
||||
/*++
|
||||
|
||||
Routine Description:
|
||||
|
||||
Register the provider with ETW+.
|
||||
|
||||
Arguments:
|
||||
|
||||
None
|
||||
|
||||
Remarks:
|
||||
|
||||
ERROR_SUCCESS if success or if the provider was already registered.
|
||||
Otherwise, an error code.
|
||||
|
||||
--*/
|
||||
#define EventRegisterRBLX_1465F7BC() EtxRegister(&RBLX_1465F7BCProvider, &RBLX_1465F7BCHandle)
|
||||
|
||||
/*++
|
||||
|
||||
Routine Description:
|
||||
|
||||
Unregister the provider from ETW+.
|
||||
|
||||
Arguments:
|
||||
None
|
||||
Remarks:
|
||||
ERROR_SUCCESS if success or if the provider was not registered.
|
||||
Otherwise, an error code.
|
||||
--*/
|
||||
#define EventUnregisterRBLX_1465F7BC() EtxUnregister(&RBLX_1465F7BCProvider, &RBLX_1465F7BCHandle)
|
||||
|
||||
#define EventEnabledA_DeleteMe() (TRUE)
|
||||
|
||||
// Entry point to log the event A_DeleteMe
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteA_DeleteMe(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_A_DeleteMe 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_A_DeleteMe];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[0], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_A_DeleteMe, EventData);
|
||||
}
|
||||
#define EventEnabledAvatarsEquipped() (TRUE)
|
||||
|
||||
// Entry point to log the event AvatarsEquipped
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAvatarsEquipped(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int AvatarsCount)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AvatarsEquipped 4
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AvatarsEquipped];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], &AvatarsCount, sizeof(AvatarsCount));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[1], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AvatarsEquipped, EventData);
|
||||
}
|
||||
#define EventEnabledAward10DayRoll() (TRUE)
|
||||
|
||||
// Entry point to log the event Award10DayRoll
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAward10DayRoll(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_Award10DayRoll 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_Award10DayRoll];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[2], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_Award10DayRoll, EventData);
|
||||
}
|
||||
#define EventEnabledAward20DayRoll() (TRUE)
|
||||
|
||||
// Entry point to log the event Award20DayRoll
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAward20DayRoll(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_Award20DayRoll 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_Award20DayRoll];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[3], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_Award20DayRoll, EventData);
|
||||
}
|
||||
#define EventEnabledAward3DayRoll() (TRUE)
|
||||
|
||||
// Entry point to log the event Award3DayRoll
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAward3DayRoll(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_Award3DayRoll 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_Award3DayRoll];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[4], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_Award3DayRoll, EventData);
|
||||
}
|
||||
#define EventEnabledAwardDeepDiver() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardDeepDiver
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardDeepDiver(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardDeepDiver 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardDeepDiver];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[5], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardDeepDiver, EventData);
|
||||
}
|
||||
#define EventEnabledAwardFoursCompany() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardFoursCompany
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardFoursCompany(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardFoursCompany 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardFoursCompany];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[6], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardFoursCompany, EventData);
|
||||
}
|
||||
#define EventEnabledAwardOneNameManyFaces() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardOneNameManyFaces
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardOneNameManyFaces(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardOneNameManyFaces 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardOneNameManyFaces];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[7], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardOneNameManyFaces, EventData);
|
||||
}
|
||||
#define EventEnabledAwardPollster() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardPollster
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardPollster(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardPollster 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardPollster];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[8], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardPollster, EventData);
|
||||
}
|
||||
#define EventEnabledAwardSampler() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardSampler
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardSampler(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardSampler 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardSampler];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[9], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardSampler, EventData);
|
||||
}
|
||||
#define EventEnabledAwardStrengthInNumbers() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardStrengthInNumbers
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardStrengthInNumbers(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardStrengthInNumbers 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardStrengthInNumbers];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[10], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardStrengthInNumbers, EventData);
|
||||
}
|
||||
#define EventEnabledAwardWorldTraveler() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardWorldTraveler
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardWorldTraveler(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardWorldTraveler 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardWorldTraveler];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[11], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardWorldTraveler, EventData);
|
||||
}
|
||||
#define EventEnabledAwardYouDidIt() (TRUE)
|
||||
|
||||
// Entry point to log the event AwardYouDidIt
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteAwardYouDidIt(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_AwardYouDidIt 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_AwardYouDidIt];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[12], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_AwardYouDidIt, EventData);
|
||||
}
|
||||
#define EventEnabledCurrentAvatar() (TRUE)
|
||||
|
||||
// Entry point to log the event CurrentAvatar
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteCurrentAvatar(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_CurrentAvatar 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_CurrentAvatar];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[13], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_CurrentAvatar, EventData);
|
||||
}
|
||||
#define EventEnabledGameProgress() (TRUE)
|
||||
|
||||
// Entry point to log the event GameProgress
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteGameProgress(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const float CompletionPercent)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_GameProgress 4
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_GameProgress];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], &CompletionPercent, sizeof(CompletionPercent));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[14], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_GameProgress, EventData);
|
||||
}
|
||||
#define EventEnabledGamesCount() (TRUE)
|
||||
|
||||
// Entry point to log the event GamesCount
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteGamesCount(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_GamesCount 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_GamesCount];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[15], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_GamesCount, EventData);
|
||||
}
|
||||
#define EventEnabledMultiplayerRoundEnd() (TRUE)
|
||||
|
||||
// Entry point to log the event MultiplayerRoundEnd
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteMultiplayerRoundEnd(__in_opt PCWSTR UserId, __in LPCGUID RoundId, __in const signed int SectionId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int MatchTypeId, __in const signed int DifficultyLevelId, __in const float TimeInSeconds, __in const signed int ExitStatusId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_MultiplayerRoundEnd 11
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_MultiplayerRoundEnd];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], RoundId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], &SectionId, sizeof(SectionId));
|
||||
EventDataDescCreate(&EventData[4], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[5], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[6], &GameplayModeId, sizeof(GameplayModeId));
|
||||
EventDataDescCreate(&EventData[7], &MatchTypeId, sizeof(MatchTypeId));
|
||||
EventDataDescCreate(&EventData[8], &DifficultyLevelId, sizeof(DifficultyLevelId));
|
||||
EventDataDescCreate(&EventData[9], &TimeInSeconds, sizeof(TimeInSeconds));
|
||||
EventDataDescCreate(&EventData[10], &ExitStatusId, sizeof(ExitStatusId));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[16], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_MultiplayerRoundEnd, EventData);
|
||||
}
|
||||
#define EventEnabledMultiplayerRoundStart() (TRUE)
|
||||
|
||||
// Entry point to log the event MultiplayerRoundStart
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWriteMultiplayerRoundStart(__in_opt PCWSTR UserId, __in LPCGUID RoundId, __in const signed int SectionId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int MatchTypeId, __in const signed int DifficultyLevelId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_MultiplayerRoundStart 9
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_MultiplayerRoundStart];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], RoundId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], &SectionId, sizeof(SectionId));
|
||||
EventDataDescCreate(&EventData[4], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[5], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[6], &GameplayModeId, sizeof(GameplayModeId));
|
||||
EventDataDescCreate(&EventData[7], &MatchTypeId, sizeof(MatchTypeId));
|
||||
EventDataDescCreate(&EventData[8], &DifficultyLevelId, sizeof(DifficultyLevelId));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[17], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_MultiplayerRoundStart, EventData);
|
||||
}
|
||||
#define EventEnabledPartyCount() (TRUE)
|
||||
|
||||
// Entry point to log the event PartyCount
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWritePartyCount(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_PartyCount 3
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_PartyCount];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[18], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_PartyCount, EventData);
|
||||
}
|
||||
#define EventEnabledPlayerSessionEnd() (TRUE)
|
||||
|
||||
// Entry point to log the event PlayerSessionEnd
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWritePlayerSessionEnd(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId, __in const signed int ExitStatusId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionEnd 7
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionEnd];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[4], &GameplayModeId, sizeof(GameplayModeId));
|
||||
EventDataDescCreate(&EventData[5], &DifficultyLevelId, sizeof(DifficultyLevelId));
|
||||
EventDataDescCreate(&EventData[6], &ExitStatusId, sizeof(ExitStatusId));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[19], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionEnd, EventData);
|
||||
}
|
||||
#define EventEnabledPlayerSessionPause() (TRUE)
|
||||
|
||||
// Entry point to log the event PlayerSessionPause
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWritePlayerSessionPause(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionPause 4
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionPause];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[20], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionPause, EventData);
|
||||
}
|
||||
#define EventEnabledPlayerSessionResume() (TRUE)
|
||||
|
||||
// Entry point to log the event PlayerSessionResume
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWritePlayerSessionResume(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionResume 6
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionResume];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[4], &GameplayModeId, sizeof(GameplayModeId));
|
||||
EventDataDescCreate(&EventData[5], &DifficultyLevelId, sizeof(DifficultyLevelId));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[21], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionResume, EventData);
|
||||
}
|
||||
#define EventEnabledPlayerSessionStart() (TRUE)
|
||||
|
||||
// Entry point to log the event PlayerSessionStart
|
||||
//
|
||||
__inline
|
||||
ULONG
|
||||
EventWritePlayerSessionStart(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId)
|
||||
{
|
||||
#define ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionStart 6
|
||||
|
||||
EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionStart];
|
||||
UINT8 scratch[64];
|
||||
|
||||
EtxFillCommonFields_v7(&EventData[0], scratch, 64);
|
||||
|
||||
EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID));
|
||||
EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L""));
|
||||
EventDataDescCreate(&EventData[4], &GameplayModeId, sizeof(GameplayModeId));
|
||||
EventDataDescCreate(&EventData[5], &DifficultyLevelId, sizeof(DifficultyLevelId));
|
||||
|
||||
return EtxEventWrite(&RBLX_1465F7BCEvents[22], &RBLX_1465F7BCProvider, RBLX_1465F7BCHandle, ARGUMENT_COUNT_RBLX_1465F7BC_PlayerSessionStart, EventData);
|
||||
}
|
||||
#if defined(__cplusplus)
|
||||
};
|
||||
#endif
|
||||
|
||||
#pragma pack(pop)
|
||||
@@ -0,0 +1,9 @@
|
||||
How to update xdpevents.h
|
||||
|
||||
Go to XDP, expand RBLX.O Development on the sidebar, select Events & Stat Rules
|
||||
Click Publish & Download, save the .man file as 'Events-RBLX.0-1465F7BC.man' in this directory.
|
||||
From Durango XDK command prompt, run this command:
|
||||
|
||||
xdpevents.bat
|
||||
|
||||
|
||||
Reference in New Issue
Block a user