notXCS/xcs-template.rbxmx

4118 lines
145 KiB
Plaintext

<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
<Meta name="ExplicitAutoJoints">true</Meta>
<External>null</External>
<External>nil</External>
<Item class="Model" referent="RBX9F347F49A8384E3492F3E36C26FA4EB3">
<Properties>
<token name="LevelOfDetail">0</token>
<CoordinateFrame name="ModelMeshCFrame">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<SharedString name="ModelMeshData">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
<Vector3 name="ModelMeshSize">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<token name="ModelStreamingMode">0</token>
<bool name="NeedsPivotMigration">false</bool>
<Ref name="PrimaryPart">null</Ref>
<float name="ScaleFactor">1</float>
<SharedString name="SlimHash">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
<OptionalCoordinateFrame name="WorldPivotData">
<CFrame>
<X>17.5</X>
<Y>4.171</Y>
<Z>-26.4860001</Z>
<R00>8.47889225e-08</R00>
<R01>0.207909048</R01>
<R02>-0.978148162</R02>
<R10>4.37113883e-08</R10>
<R11>0.978148162</R11>
<R12>0.207909048</R12>
<R20>1</R20>
<R21>-6.03846004e-08</R21>
<R22>7.38481347e-08</R22>
</CFrame>
</OptionalCoordinateFrame>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Restrafes XCS</string>
<int64 name="SourceAssetId">7108324788</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="ModuleScript" referent="RBXA7C4B4F1335A43858D376EB6A739C7D4">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[--[[
NotXCS
Original by RESTRAFES & CO.
---------------------------------------------------
This is the configuration file for the entire location
-and should be placed in ServerScriptService.
This script has been automatically configured for jkujmkj.
To configure access points for this location, visit:
https://notxcs.rtech.sh
--]]
local settings = {
["url"] = "https://notxcs.rtech.sh/api/v1/ingame";
["placeId"] = "%%PLACEID%%";
["apiKey"] = "%%APIKEY%%";
["syncInterval"] = 30;
["proximityPromptAccessWithoutCard"] = true;
["debug"] = false;
["evolution"] = 0x1;
};
return settings;]]></ProtectedString>
<string name="ScriptGuid">{B88BC829-2E70-458E-9567-27196412A674}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">config</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Script" referent="RBX3CA3B3822FA34F018F0291D27677871C">
<Properties>
<ProtectedString name="Source"><![CDATA[local loader = require(script.xcssrc);
local _m = require(loader({["branch"]="xcs-superior";}));
_m.init({["config"]=require(script.Parent);});
]]></ProtectedString>
<bool name="Disabled">false</bool>
<Content name="LinkedSource"><null></null></Content>
<token name="RunContext">0</token>
<string name="ScriptGuid">{CD756DAA-C3CA-4FCF-8E82-33ACB5E578B6}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">loader</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="ModuleScript" referent="RBX48C33D1F87AF4B4FA3A328EB54F70CDC">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[--[[
This code is for XCS, a Roblox access control framework that allows experiences to verify users, manage permissions, and control access to doors, areas, systems, and protected features through configurable roles and access groups. HTTPService is used to securely communicate with the XCS backend, allowing the game to check access permissions, validate users, and retrieve the latest access control data.
]]
local sss = game.ServerScriptService;
return function(t)
if t["branch"] == "axesys-reader" then
local scriptClone = script.unitAxesys:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "axesys-superior" then
local scriptClone = script.controllerAxesys:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "xcs-reader" then
local scriptClone = script.unit:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "xcs-superior" then
local scriptClone = script.controller:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "httpm" then
local httpmClone = script.httpm:Clone();
httpmClone.Parent = nil;
httpmClone.Name = math.random(9*100,10*200);
httpmClone.Parent = sss;
httpmClone.Disabled = false;
return;
end
string.format("XCS - You've entered an invalid branch (%s). Please check your configuration and try again.", t["branch"])
end
]]></ProtectedString>
<string name="ScriptGuid">{ef7612db-42e6-4525-8e79-9c583fec2bff}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">xcssrc</string>
<int64 name="SourceAssetId">7108284356</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Script" referent="RBX63B2CC09B51E4555A23E59D73A67B01D">
<Properties>
<ProtectedString name="Source"><![CDATA[local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local MessageGui = script.XCS_HTTP
for _,P in pairs(Players:GetChildren()) do
local Msg = MessageGui:Clone()
Msg.Parent = P.PlayerGui
end
Players.PlayerAdded:Connect(function(Player)
local Msg = MessageGui:Clone()
Msg.Parent = Player.PlayerGui
end)
]]></ProtectedString>
<bool name="Disabled">true</bool>
<Content name="LinkedSource"><null></null></Content>
<token name="RunContext">0</token>
<string name="ScriptGuid">{cd26dcdf-c1c0-4c32-a66e-890426c8e596}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">httpm</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="ScreenGui" referent="RBX8B3CCD0295FA4351B500A36E08E85DF4">
<Properties>
<bool name="ClipToDeviceSafeArea">true</bool>
<int name="DisplayOrder">0</int>
<token name="SafeAreaCompatibility">1</token>
<token name="ScreenInsets">2</token>
<bool name="Enabled">true</bool>
<bool name="ResetOnSpawn">false</bool>
<token name="ZIndexBehavior">0</token>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">XCS_HTTP</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Frame" referent="RBXA43D6C7D129445139AAEA040F7170CC5">
<Properties>
<token name="Style">0</token>
<bool name="Active">false</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>0</R>
<G>0</G>
<B>0</B>
</Color3>
<float name="BackgroundTransparency">0</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0.5</XS>
<XO>-300</XO>
<YS>0</YS>
<YO>0</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>600</XO>
<YS>0</YS>
<YO>50</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Frame</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="ImageButton" referent="RBXB7800919118F44FC89B364585232E943">
<Properties>
<Content name="HoverImage"><null></null></Content>
<Content name="Image"><url>http://www.roblox.com/asset/?id=14511145204</url></Content>
<Color3 name="ImageColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<Vector2 name="ImageRectOffset">
<X>0</X>
<Y>0</Y>
</Vector2>
<Vector2 name="ImageRectSize">
<X>0</X>
<Y>0</Y>
</Vector2>
<float name="ImageTransparency">0</float>
<Content name="PressedImage"><null></null></Content>
<token name="ResampleMode">0</token>
<token name="ScaleType">3</token>
<Rect2D name="SliceCenter">
<min>
<X>0</X>
<Y>0</Y>
</min>
<max>
<X>0</X>
<Y>0</Y>
</max>
</Rect2D>
<float name="SliceScale">1</float>
<UDim2 name="TileSize">
<XS>1</XS>
<XO>0</XO>
<YS>1</YS>
<YO>0</YO>
</UDim2>
<bool name="AutoButtonColor">false</bool>
<Ref name="HoverHapticEffect">null</Ref>
<bool name="Modal">false</bool>
<Ref name="PressHapticEffect">null</Ref>
<bool name="Selected">false</bool>
<token name="Style">0</token>
<bool name="Active">true</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0.5</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<float name="BackgroundTransparency">1</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0</XS>
<XO>10</XO>
<YS>0.5</YS>
<YO>0</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>80</XO>
<YS>0</YS>
<YO>18</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Logo</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="TextLabel" referent="RBX91C7E5F3F40440F5947E595D37EF1442">
<Properties>
<Font name="FontFace">
<Family><url>rbxasset://fonts/families/Roboto.json</url></Family>
<Weight>400</Weight>
<Style>Normal</Style>
</Font>
<float name="LineHeight">1</float>
<string name="LocalizationMatchIdentifier"></string>
<string name="LocalizationMatchedSourceText"></string>
<int name="MaxVisibleGraphemes">-1</int>
<string name="OpenTypeFeatures"></string>
<bool name="RichText">false</bool>
<string name="Text">HttpService is required, please enable it in game settings.</string>
<Color3 name="TextColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<token name="TextDirection">0</token>
<bool name="TextScaled">false</bool>
<float name="TextSize">18</float>
<Color3 name="TextStrokeColor3">
<R>0</R>
<G>0</G>
<B>0</B>
</Color3>
<float name="TextStrokeTransparency">1</float>
<float name="TextTransparency">0</float>
<token name="TextTruncate">0</token>
<bool name="TextWrapped">true</bool>
<token name="TextXAlignment">0</token>
<token name="TextYAlignment">0</token>
<bool name="Active">false</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<float name="BackgroundTransparency">1</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0</XS>
<XO>100</XO>
<YS>0</YS>
<YO>25</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>500</XO>
<YS>0</YS>
<YO>25</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Line2</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="TextLabel" referent="RBXE587EA841A9C40209668257F5FA0F478">
<Properties>
<Font name="FontFace">
<Family><url>rbxasset://fonts/families/Roboto.json</url></Family>
<Weight>400</Weight>
<Style>Normal</Style>
</Font>
<float name="LineHeight">1</float>
<string name="LocalizationMatchIdentifier"></string>
<string name="LocalizationMatchedSourceText"></string>
<int name="MaxVisibleGraphemes">-1</int>
<string name="OpenTypeFeatures"></string>
<bool name="RichText">false</bool>
<string name="Text">Restrafes XCS Controller Error</string>
<Color3 name="TextColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<token name="TextDirection">0</token>
<bool name="TextScaled">false</bool>
<float name="TextSize">18</float>
<Color3 name="TextStrokeColor3">
<R>0</R>
<G>0</G>
<B>0</B>
</Color3>
<float name="TextStrokeTransparency">1</float>
<float name="TextTransparency">0</float>
<token name="TextTruncate">0</token>
<bool name="TextWrapped">true</bool>
<token name="TextXAlignment">0</token>
<token name="TextYAlignment">2</token>
<bool name="Active">false</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<float name="BackgroundTransparency">1</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0</XS>
<XO>100</XO>
<YS>0</YS>
<YO>0</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>500</XO>
<YS>0</YS>
<YO>25</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Line1</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
</Item>
</Item>
</Item>
<Item class="ModuleScript" referent="RBX91DBD138F6714B1C9AF6D2CE271D4DFE">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
_m.config = nil;
_m.apiReady = false;
local http = game:GetService("HttpService");
local dss = game:GetService("DataStoreService");
local datastore = dss:GetDataStore("axesys_persistence");
local backupDB = false;
local utd_fetch = nil;
local readers = {};
local moduleId = 0;
local f = string.format;
local str = tostring;
local basePrint = print
local baseWarn = warn
local print = function(...)
if (not _m.config["debug"]) then return; end
basePrint(f("%s%s", "Restrafes XCS: ", str(...)));
end
local warn = function(...)
if (not _m.config["debug"]) then return; end
baseWarn(f("%s%s", "Restrafes XCS: ", str(...)));
end
function _m.main()
_m.apiReady = false;
local storeData, fetchData = nil, nil;
local success, errorMessage = pcall(function()
fetchData = http:GetAsync(_m.url.."/syncdoors/".._m.config["apiKey"].."/".._m.config["placeId"]);
end)
if (success) then
print("successfully fetched fresh data from the axesys service");
storeData = http:JSONDecode(fetchData);
utd_fetch = storeData["data"];
_m.apiReady = true;
else
print("error while fetching data from to the axesys service / "..errorMessage);
print("will try the next sync in ".._m.config["syncInterval"]);
print("Using local datastore cache");
_m.apiReady = true
backupDB = true
end
if (not backupDB) then
local success,errorMessage = pcall(function()
for doorId,doorData in pairs(utd_fetch) do
datastore:SetAsync(doorId, doorData);
end
end)
if (success) then
print("successfully updated local datastore with fresh data from the axesys service");
else
print("failed to save door data locally. / "..errorMessage);
end
end
wait(_m.config["syncInterval"]);
_m.main();
end
function _m.init(data)
_m.config = data["config"];
_m.api = _G.axesysApi or Instance.new("BindableFunction");
_m.api.Parent = nil;
_m.api.Name = "axesysApi";
_G.axesysApi = _m.api;
assert(_m.config["url"],"'url' not set!");
assert(_m.config["placeId"],"'placeId' not set!");
assert(_m.config["apiKey"],"'apiKey' not set");
assert(_m.config["syncInterval"],"'syncInterval' not set");
_m.url = _m.config["url"];
-- test connection w/ provided url
local success, errorMsg = pcall(function()
local testConnection = http:GetAsync(_m.url.."/Ping")
if (testConnection == "Pong!") then
print("successfully connected to the server!");
else
assert(false, testConnection);
end
end)
if (not success) then
if (errorMsg == "Http requests are not enabled. Enable via game settings") then
require(moduleId)("httpm");
return
end
end
-- api implementation
_m.api.OnInvoke = function(data)
if (not _m.apiReady) then repeat wait() until _m.apiReady end;
if (type(data) == "table") then
if (not backupDB and utd_fetch[data["reader"]] ~= nil or datastore:GetAsync(data["reader"]) ~= nil) then
if (data["request"] == "ping") then -- api connection
return {["response"]=true;};
end
if (data["request"] == "settings") then -- fetch settings for a door (active, locked, timer duration)
if (not backupDB) then
return {["response"]="settings";["data"]=utd_fetch[data["reader"]]["DoorSettings"];};
else
return {["response"]="settings";["data"]=datastore:GetAsync(data["reader"])["DoorSettings"];};
end
end
if (data["request"] == "checkAccess") then -- main checkpoint function
local uid = tostring(data["uid"]);
local doorData = nil;
if (not backupDB) then
doorData = utd_fetch[data["reader"]]
else
doorData = datastore:GetAsync(data["reader"])
end
coroutine.resume(coroutine.create(function() http:GetAsync(_m.url.."/addlog/".._m.apiKey.."/".._m.config["apiKey"].."/"..data["reader"].."/"..data["uid"]) end))
-- check if the user requesting access is in the list of authorized users
for uid_l,username in pairs(doorData["AuthorizedUsers"]) do
if (uid_l == uid) then
return {["response"]="access";["data"]=true;}
end
end
return {["response"]="access";["data"]=false;}
end
else
return {["response"]="invalid-reader";}
end
else
assert(false, "invalid data sent to the api!");
end
end
local proxService = game:GetService("ProximityPromptService");
proxService.PromptTriggered:Connect(function(obj, player)
if (obj.Name == "xcsInteraction") then
local findApi = obj.Parent.Parent:FindFirstChild("API");
if (findApi) then
-- if proximityPrompAccessWithoutCard is disabled, always deny the request
findApi:Fire({["request"]="onScan";["player"]=player;["isProximityPrompt"]=true;["proximityPrompAccessWithoutCard"]=_m.config["proximityPrompAccessWithoutCard"];["proximityObject"]=obj;});
end
end
end)
_m.main();
print("error while trying to connect to the axesys service / "..errorMsg);
print("trying again in 60 seconds but will use backup database");
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{8416f780-f0d8-4765-95fe-36fa95288001}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">controllerAxesys</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ModuleScript" referent="RBX9E7C4C38191945A489457AD24665756B">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
sss = game:GetService("ServerScriptService");
_m.config = nil;
_m.model = nil;
_m.doorId = nil; _m.doorLocked = nil; _m.doorActive = false;
_m.doorTimer = 5;
_m.isSyncing = false;
_m.syncTimerStarted = false;
_m.cardScan = false;
repeat wait(1) until _G.axesysApi ~= nil;
_m.superiorApi = _G.axesysApi;
function _m._legacy_trigger(newValue)
if (_m.model:FindFirstChild("Trigger")) then
_m.model:FindFirstChild("Trigger").Value = newValue;
end
end
function _m.negotiate(player, proximityObject)
if (_m.doorLocked and not _m.cardScan and _m.doorActive) then
if (_m.isSyncing) then return; end
_m.cardScan = true
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
local checkUser = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="checkAccess";["uid"]=player.UserId})["data"];
local accessGranted = (checkUser and true or false);
if (accessGranted) then
spawn(function()
_m.events["accessGranted"](player);
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=true;["player"]=player;});
_m.api:Fire({["request"]="onAccessGranted";["player"]=player;["timerLength"]=_m.doorTimer;});
if (_m.sound) then
_m.sound.SoundId = _m.sounds["accessGranted"];
_m.sound:Play();
end
if (proximityObject) then
proximityObject.Enabled = false;
end
local time = 0;
repeat
wait(1);
time += 1;
local syncTimed = _m.doorTimer;
until time > syncTimed
spawn(function()
_m.events["accessReady"]();
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=false;["player"]=player;});
if (proximityObject) then
proximityObject.Enabled = true;
end
else
spawn(function()
_m.events["accessDenied"](player);
end)
_m.api:Fire({["request"]="onAccessDenied";["player"]=player;["timerLength"]=_m.config["accessDeniedTimer"];});
if (_m.sound) then
_m.sound.SoundId = _m.sounds["accessDenied"];
_m.sound:Play();
end
if (proximityObject) then
proximityObject.Enabled = false;
end
wait(_m.config["accessDeniedTimer"]);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
if (proximityObject) then
proximityObject.Enabled = true;
end
end
_m.cardScan = false
end
end
function _m.touchScan(x)
if (x.Parent:FindFirstChild("CardNumber") or x.Parent:FindFirstChild("_axesysCard") and x.Name == "Handle" and x.Parent.ClassName == "Tool") then
local player = game.Players:GetPlayerFromCharacter(x.Parent.Parent);
local uid = tostring(player.UserId);
_m.negotiate(player);
end
end
function readerSettings()
local newSettings = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="settings"})["data"];
_m.doorActive = (newSettings["Active"] == "1" and true or false);
_m.doorLocked = (newSettings["Locked"] == "1" and true or false);
_m.api:Fire({["request"]="setDoorTrigger";["value"]=(newSettings["Locked"] == "0" and true or false);["player"]=player;});
_m.indicatorChange("LEDR",(newSettings["Active"] == "0" and 1 or 0));
_m.indicatorChange("LEDG",(newSettings["Locked"] == "1" and 0 or 1));
_m.indicatorChange("LEDS",(newSettings["Active"] == "1" and newSettings["Locked"] == "1" and 1 or 0));
if (_m.doorActive and _m.doorLocked) then
spawn(function()
_m.events["accessReady"]();
end)
elseif (_m.doorActive and not _m.doorLocked) then
spawn(function()
_m.events["enabled"]();
end)
elseif (not _m.doorActive) then
spawn(function()
_m.events["disabled"]();
end)
end
_m.doorTimer = tonumber(newSettings["Timer"]);
wait(60);
readerSettings();
end
function _m.indicatorChange(N,S)
for _,x in pairs(_m.model:GetChildren()) do
if (x.Name == N) then
x.BrickColor = BrickColor.new(_m.config["_legacy_indicators"][N][S])
if (S == 1) then
x.Material = "Neon";
else
x.Material = "SmoothPlastic";
end
end
end
end
function _m.init(data)
wait(2.5)
_m.config = data["config"];
_m.model = _m.config["model"];
_m.doorId = _m.config["doorId"];
_m.sounds = _m.config["sounds"];
_m.events = _m.config["events"];
_m.sound = nil;
_m.api = _m.model.API;
if (_m.model:FindFirstChild("_main")) then
_m.sound = _m.model:FindFirstChild("_main"):FindFirstChild("sound");
end
if (_m.config["_legacy_indicators"] == nil) then
_m.config["_legacy_indicators"] = {
["LEDR"]={[0]="Really black",[1]="Really red"};
["LEDG"]={[0]="Really black",[1]="Lime green"};
["LEDS"]={[0]="Really black",[1]="Institutional white"};
};
end
if (_m.events["accessGranted"] == nil) then
_m.events["accessGranted"] = function(player)
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessDenied"] == nil) then
_m.events["accessDenied"] = function(player)
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["enabled"] == nil) then
_m.events["enabled"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["disabled"] == nil) then
_m.events["disabled"] = function()
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessReady"] == nil) then
_m.events["accessReady"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
end
end
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
local startUp = true;
repeat
_m.indicatorChange("LEDR",1)
local waitPing = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="ping";})
if (waitPing["response"]) then
startUp = false;
break;
end
if (waitPing["response"] == "invalid-reader") then
_m.indicatorChange("LEDR",1);
assert(false,"eve axesys (reader): [".._m.doorId.."]: invalid reader!");
end
until not startUp
_m.indicatorChange("LEDR",0);
wait(0.2);
_m.indicatorChange("LEDR",1);
wait(0.2);
_m.indicatorChange("LEDG",1);
wait(0.2);
_m.indicatorChange("LEDS",1);
wait(2.5);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
if (_m.model:FindFirstChild("_main")) then
_m.model:FindFirstChild("_main").Touched:connect(_m.touchScan);
end
-- new interaction method
_m.api.Event:Connect(function(data)
if (data["request"] == "onScan") then
if (data["isProximityPrompt"]) then
if (data["proximityPrompAccessWithoutCard"]) then
_m.negotiate(data["player"]);
else
for _,v in pairs(data["player"]:FindFirstChild("Backpack"):GetChildren()) do
if (v:FindFirstChild("CardNumber") or v:FindFirstChild("_axesysCard") and v.Parent.ClassName == "Tool") then
_m.negotiate(data["player"], data["proximityObject"]);
return;
end
end
for _,v in pairs(data["player"].Character:GetChildren()) do
if (v:FindFirstChild("CardNumber") or v:FindFirstChild("_axesysCard") and v.Parent.ClassName == "Tool") then
_m.negotiate(data["player"], data["proximityObject"]);
return;
end
end
end
else
_m.negotiate(data["player"]);
end
elseif (data["request"] == "setDoorTrigger") then
_m._legacy_trigger(data["value"]);
end
end)
readerSettings()
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{2ca9fb29-cd45-4b62-b6c1-f333e2d89b32}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">unitAxesys</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ModuleScript" referent="RBX9F79E55061914B36B03B5D4D0DDEED5B">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
_m.config = nil;
_m.apiReady = false;
local http = game:GetService("HttpService");
local dss = game:GetService("DataStoreService");
local MessagingService = game:GetService("MessagingService");
local Players = game:GetService("Players");
local datastore = dss:GetDataStore("xcs_storage");
local useDataStoreBackup = false;
local utd_fetch = nil;
local readers = {};
local f = string.format;
local str = tostring;
local basePrint = print
local baseWarn = warn
local print = function(...)
if (not _m.config["debug"]) then return; end
basePrint(f("%s%s", "Restrafes XCS: ", str(...)));
end
local warn = function(...)
if (not _m.config["debug"]) then return; end
baseWarn(f("%s%s", "Restrafes XCS: ", str(...)));
end
function _m.main()
coroutine.wrap(function()
--_m.apiReady = false;
local storeData, fetchData = nil, nil;
local success, errorMessage = pcall(function()
local url = string.format(_m.url .. "/%s?apiKey=%s&universeId=%s", _m.config["placeId"], _m.config["apiKey"], game.GameId);
local response = http:RequestAsync({
["Url"] = url;
["Method"] = "GET";
["Headers"] = {
["Content-Type"] = "application/json";
};
});
if (response["Success"]) then
storeData = http:JSONDecode(response["Body"]);
print("Successfully retrieved up-to-date data from the XCS service.");
utd_fetch = storeData["accessPoints"];
if (utd_fetch == nil and type(storeData["location"]) == "table") then
utd_fetch = {};
for _, accessPoint in ipairs(storeData["location"]["accessPoints"] or {}) do
local configuration = accessPoint["configuration"] or {};
local armedState = configuration["armed"];
local armed = (armedState == "ARMED" or armedState == true);
utd_fetch[accessPoint["id"]] = {
["config"] = {
["active"] = configuration["active"];
["armed"] = armed;
["unlockTime"] = configuration["unlockTime"];
["scanData"] = {
["ready"] = configuration["scanDataArmed"] or {};
["disarmed"] = configuration["scanDataDisarmed"] or {};
};
};
};
end
end
if (utd_fetch == nil) then
error("Response did not include access point data");
end
_m.apiReady = true;
else
local msg = "Unknown Error";
local code = "Unknwon Code";
pcall(function()
local decodedBody = http:JSONDecode(response["Body"]);
msg = decodedBody["message"];
code = response["StatusCode"];
end)
warn(string.format("Received an error %s response while trying to retrieve data from the XCS service: %s", code, msg));
warn(string.format("Will try the next sync in %s seconds...\nUsing the backup datastore in the meantime.", _m.config["syncInterval"]));
_m.apiReady = true
useDataStoreBackup = true
end
end)
if (not success) then
warn("Failed to sync access point data from the XCS service: " .. tostring(errorMessage));
warn(string.format("Will try the next sync in %s seconds...\nUsing the backup datastore in the meantime.", _m.config["syncInterval"]));
_m.apiReady = true;
useDataStoreBackup = true;
end
if (not useDataStoreBackup and utd_fetch ~= nil) then
local success, errorMessage = pcall(function()
datastore:SetAsync("accessPoints", utd_fetch);
end)
if (success) then
print("Successfully updated the local datastore with fresh data from the XCS service.");
else
print("Failed to save access point data locally: " ..errorMessage);
end
end
wait(_m.config["syncInterval"]);
_m.main();
end)()
end
function _m.init(data)
_m.config = data["config"];
_m.api = _G.xcsApi or Instance.new("BindableFunction");
_m.api.Parent = nil;
_m.api.Name = "XCS";
_G.xcsApi = _m.api;
assert(_m.config["url"], "'url' not set!");
assert(_m.config["placeId"], "'placeId' not set!");
assert(_m.config["apiKey"], "'apiKey' not set!");
assert(_m.config["syncInterval"], "'syncInterval' not set!");
_m.url = _m.config["url"];
if (_G.xcsRemoteUnlock == nil) then
_G.xcsRemoteUnlock = Instance.new("BindableEvent");
_G.xcsRemoteUnlock.Name = "XCSRemoteUnlock";
end
local unlockTopic = "xcs-unlock-" .. _m.config["placeId"];
pcall(function()
MessagingService:SubscribeAsync(unlockTopic, function(message)
local payload = nil;
pcall(function()
payload = http:JSONDecode(message.Data);
end);
if (type(payload) ~= "table" or payload["v"] ~= 1) then
warn("Ignored remote unlock message: invalid payload version");
return;
end
if (payload["locationId"] ~= _m.config["placeId"]) then
warn("Ignored remote unlock message: locationId mismatch");
return;
end
if (payload["scope"] == "player") then
local uid = tonumber(payload["robloxUserId"]);
if (uid == nil or Players:GetPlayerByUserId(uid) == nil) then
warn("Ignored player-scoped remote unlock: target player not in this server");
return;
end
end
local apId = payload["accessPointId"];
if (apId == nil) then
warn("Ignored remote unlock message: missing accessPointId");
return;
end
-- Platform publish is authoritative; do not gate on utd_fetch sync state.
_G.xcsRemoteUnlock:Fire(apId, payload);
if (payload["scope"] == "player" and payload["requestId"]) then
spawn(function()
pcall(function()
local ackUrl = string.format(
"%s/%s/%s/remote-unlock/ack?apiKey=%s&requestId=%s",
_m.url,
_m.config["placeId"],
apId,
_m.config["apiKey"],
payload["requestId"]
);
local response = http:RequestAsync({
["Url"] = ackUrl;
["Method"] = "POST";
["Headers"] = {
["Content-Type"] = "application/json";
};
});
if (not response["Success"]) then
warn(string.format(
"Remote unlock ack failed (%s): %s",
tostring(response["StatusCode"]),
tostring(response["Body"])
));
end
end);
end);
end
end);
print("Subscribed to remote unlock topic: " .. unlockTopic);
end);
-- test connection w/ provided url
local success, errorMsg = pcall(function()
local testConnection = http:GetAsync(_m.url .. "/ping")
testConnection = http:JSONDecode(testConnection);
if (testConnection.success) then
print("Successfully connected to the server!");
else
assert(false, testConnection);
end
end)
if (not success) then
if (errorMsg ==
"Http requests are not enabled. Enable via game settings") then
require(script.Parent)({["branch"]="httpm"})
error(errorMsg)
return
end
end
-- API implementation
_m.api.OnInvoke = function(data)
if (not _m.apiReady) then repeat wait() until _m.apiReady end
if (type(data) == "table") then
-- continue if either:
-- 1. useDataStoreBackup is false and the reader exists or
-- 2. useDataStoreBackup is true and the reader exists in the backup datastore
if (not useDataStoreBackup and utd_fetch[data["reader"]] ~= nil or
datastore:GetAsync("accessPoints")[data["reader"]] ~= nil) then
if (data["request"] == "ping") then -- api connection
return {["response"] = true};
end
if (data["request"] == "settings") then -- fetch settings for a door (active, locked, timer duration)
if (not useDataStoreBackup) then
if (utd_fetch[data["reader"]] == nil) then
return {["response"] = "invalid-reader"};
else
return {
["response"] = "settings";
["data"] = utd_fetch[data["reader"]]["config"];
};
end
else
local accessPointsBackup = datastore:GetAsync("accessPoints");
if (accessPointsBackup[data["reader"]] == nil) then
return {["response"] = "invalid-reader"};
else
return {
["response"] = "settings";
["data"] = accessPointsBackup[data["reader"]]["config"];
};
end
end
end
if (data["request"] == "checkAccess") then -- main checkpoint function
local uid = tostring(data["uid"]);
local doorData = nil;
if (not useDataStoreBackup) then
doorData = utd_fetch[data["reader"]];
else
local accessPointsBackup = datastore:GetAsync("accessPoints");
doorData = accessPointsBackup[data["reader"]];
end
local fetchData = http:RequestAsync({
["Url"] = string.format(_m.url .. "/%s/%s/onScan?userId=%s&apiKey=%s&cardNumbers=%s", _m.config["placeId"], data["reader"], uid, _m.config["apiKey"], table.concat(data["cardNumbers"], ",") or "null");
["Method"] = "GET";
["Headers"] = {
["Content-Type"] = "application/json";
};
});
if (fetchData["Success"]) then
fetchData = http:JSONDecode(fetchData["Body"]);
return {["response"] = "access", ["data"] = fetchData};
else
local msg = "Unknown Error";
local code = "Unknown Code";
pcall(function()
local decodedBody = http:JSONDecode(fetchData["Body"]);
msg = decodedBody["message"];
code = fetchData["StatusCode"];
end)
warn(string.format("Received an error %s response while trying to check an access point scan from the XCS service: %s", code, msg));
return {["response"] = "access", ["data"] = {["response_code"] = "access_denied", ["scan_data"] = {["message"] = msg, ["code"] = code}}};
end
end
else
return {["response"] = "invalid-reader"}
end
else
assert(false, "invalid data sent to the api!");
end
end
local proxService = game:GetService("ProximityPromptService");
proxService.PromptTriggered:Connect(function(obj, player)
if (obj.Name == "xcsInteraction") then
local findApi = obj.Parent.Parent:FindFirstChild("API");
if (findApi) then
-- if proximityPrompAccessWithoutCard is disabled, always deny the request
findApi:Fire({
["request"] = "onScan",
["player"] = player,
["isProximityPrompt"] = true,
["proximityPromptAccessWithoutCard"] = _m.config["proximityPromptAccessWithoutCard"],
["proximityObject"] = obj
});
end
end
end)
_m.main();
-- print("Error while trying to connect to the XCS service!");
-- print("Trying again in 60 seconds..");
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{c94b318e-9267-42d2-8881-9790a1f569d9}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">controller</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ModuleScript" referent="RBX19399067A1934CCF92D4411EF4AA1AB3">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
sss = game:GetService("ServerScriptService");
_m.config = nil;
_m.model = nil;
_m.doorId = nil; _m.doorLocked = nil; _m.doorActive = false;
_m.doorTimer = 5;
_m.eventThread = nil;
_m.isSyncing = false;
_m.syncTimerStarted = false;
_m.cardScan = false;
repeat wait(1) until _G.xcsApi ~= nil;
_m.superiorApi = _G.xcsApi;
function _m.findAny(a, b)
for _,c in pairs(b) do
if (a:FindFirstChild(c)) then return a:FindFirstChild(c); end
end
return false;
end
function _m.cancelEventThread()
if (_m.eventThread) then
pcall(function()
task.cancel(_m.eventThread);
end)
_m.eventThread = nil;
end
end
function _m._legacy_trigger(newValue)
if (_m.model:FindFirstChild("Trigger")) then
_m.model:FindFirstChild("Trigger").Value = newValue;
end
end
function _m.negotiate(player, proximityObject, cardNumbers)
local s0, m0 = pcall(function()
if (_m.doorLocked and not _m.cardScan and _m.doorActive) then
if (_m.isSyncing) then return; end
_m.cardScan = true
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["pending"] or _m.sounds["accessPending"];
_m.sound:Play();
end
spawn(function()
if _m.events["accessPending"] or _m.events["pending"] then
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessPending"] or _m.events["pending"], player);
end
end)
if (proximityObject) then
proximityObject.Enabled = false;
end
local checkUser = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="checkAccess";["uid"]=player.UserId;["cardNumbers"]=cardNumbers})["data"];
local accessGranted = (checkUser["response_code"] == "access_granted" and true or false);
if (accessGranted) then
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["granted"] or _m.sounds["accessGranted"];
_m.sound:Play();
end
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessGranted"] or _m.events["granted"], player, checkUser["scan_data"]);
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=true;["player"]=player;});
_m.api:Fire({["request"]="onAccessGranted";["player"]=player;["timerLength"]=_m.doorTimer;});
local time = 0;
local syncTimed = _m.doorTimer;
repeat
wait(1);
time += 1;
until time > syncTimed
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], checkUser["scan_data"]);
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=false;["player"]=player;});
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
else
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["denied"] or _m.sounds["accessDenied"];
_m.sound:Play();
end
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessDenied"] or _m.events["denied"], player, checkUser["scan_data"]);
end)
_m.api:Fire({["request"]="onAccessDenied";["player"]=player;["timerLength"]=_m.config["accessDeniedTimer"];});
wait(_m.config["accessDeniedTimer"]);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], checkUser["scan_data"]);
end)
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
end
_m.cardScan = false
end
end)
if (not s0) then
print(string.format("There was an error while negotiating an access point: %s", m0))
end
end
function _m.touchScan(x)
if (_m.findAny(x.Parent, {"CardNumber", "_xcsCard", "XCS"}) and x.Name == "Handle" and x.Parent.ClassName == "Tool") then
local player = game.Players:GetPlayerFromCharacter(x.Parent.Parent);
local uid = tostring(player.UserId);
local cardNumber = nil;
local cardNumberObject = _m.findAny(x.Parent, {"CardNumber", "_xcsCard", "XCS"});
if (cardNumberObject and (cardNumberObject:IsA("StringValue") or cardNumberObject:IsA("NumberValue") or cardNumberObject:IsA("IntValue"))) then
cardNumber = cardNumberObject.Value;
end
_m.negotiate(player, nil, {cardNumber});
end
end
function readerSettings()
local s0, m0 = pcall(function()
spawn(function()
local proximityObject = _m.model:FindFirstChild("xcsInteraction", true);
local newSettings = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="settings"});
if (newSettings["response"] == "invalid-reader") then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessDisabled"] or _m.events["disabled"], {});
end)
assert(false, string.format("Restrafes XCS (reader): [%s]: Invalid reader ID!\n%s", _m.doorId, _m.model:GetFullName()));
end
newSettings = newSettings["data"];
_m.doorActive = (newSettings["active"]);
_m.doorLocked = (newSettings["armed"]);
_m.api:Fire({["request"]="setDoorTrigger";["value"]=(newSettings["armed"]);["player"]=player;});
_m.indicatorChange("LEDR",(newSettings["active"] == false and 1 or 0));
_m.indicatorChange("LEDG",(newSettings["armed"] == true and 0 or 1));
_m.indicatorChange("LEDS",(newSettings["active"] == true and newSettings["armed"] == true and 1 or 0));
if (_m.doorActive and _m.doorLocked) then
if (not _m.cardScan) then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], newSettings["scanData"]["ready"] or {});
end)
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
end
elseif (_m.doorActive and not _m.doorLocked) then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["enabled"] or _m.events["disarmed"], newSettings["scanData"]["disarmed"] or {});
end)
if (proximityObject) then
proximityObject.Enabled = false;
end
elseif (not _m.doorActive) then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessDisabled"] or _m.events["disabled"], {});
end)
if (proximityObject) then
proximityObject.Enabled = false;
end
end
_m.doorTimer = tonumber(newSettings["unlockTime"] or 5);
wait(15);
readerSettings();
end)
end)
if (not s0) then
print(string.format("There was an error while grabbing up-to-date reader settings: %s", m0))
end
end
function _m.indicatorChange(N,S)
for _,x in pairs(_m.model:GetChildren()) do
if (x.Name == N) then
x.BrickColor = BrickColor.new(_m.config["_legacy_indicators"][N][S])
if (S == 1) then
x.Material = "Neon";
else
x.Material = "SmoothPlastic";
end
end
end
end
function _m.remoteUnlock(unlockSeconds)
if (not _m.doorActive or _m.cardScan) then return; end
_m.cardScan = true;
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["granted"] or _m.sounds["accessGranted"];
_m.sound:Play();
end
spawn(function()
local proximityObject = _m.model:FindFirstChild("xcsInteraction", true);
if (proximityObject) then
proximityObject.Enabled = false;
end
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessGranted"] or _m.events["granted"], nil, {});
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=true;});
local time = 0;
local syncTimed = tonumber(unlockSeconds) or _m.doorTimer;
_m.api:Fire({["request"]="onAccessGranted";["timerLength"]=syncTimed;});
repeat
wait(1);
time += 1;
until time > syncTimed
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], {});
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=false;});
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
_m.cardScan = false;
end)
end
function _m.init(data)
_m.config = data["config"];
_m.model = _m.config["model"];
_m.doorId = _m.config["accessPointId"] or _m.config["doorId"];
_m.sounds = _m.config["sounds"];
_m.events = _m.config["events"];
_m.sound = nil;
_m.api = _m.model.API;
if (_m.model:FindFirstChild("_main")) then
_m.sound = _m.model:FindFirstChild("_main"):FindFirstChild("sound");
if (_m.sound and _m.sounds) then
_m.sounds["granted"] = _m.sounds["granted"] or _m.sounds["accessGranted"] or "";
_m.sounds["denied"] = _m.sounds["denied"] or _m.sounds["accessDenied"] or "";
_m.sounds["pending"] = _m.sounds["pending"] or _m.sounds["accessPending"] or "";
end
end
if (_m.config["_legacy_indicators"] == nil) then
_m.config["_legacy_indicators"] = {
["LEDR"]={[0]="Really black",[1]="Really red"};
["LEDG"]={[0]="Really black",[1]="Lime green"};
["LEDS"]={[0]="Really black",[1]="Institutional white"};
};
end
if (_m.events["accessGranted"] == nil and _m.events["granted"] == nil) then
_m.events["accessGranted"] = function(player)
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessDenied"] == nil and _m.events["denied"] == nil) then
_m.events["accessDenied"] = function(player)
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["enabled"] == nil and _m.events["disarmed"] == nil) then
_m.events["enabled"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessDisabled"] == nil and _m.events["disabled"] == nil) then
_m.events["disabled"] = function()
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessReady"] == nil and _m.events["ready"] == nil) then
_m.events["accessReady"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
end
end
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
local startUp = true;
repeat
_m.indicatorChange("LEDR",1)
local waitPing = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="ping";})
if (waitPing["response"]) then
startUp = false;
break;
end
if (waitPing["response"] == "invalid-reader") then
_m.indicatorChange("LEDR",1);
assert(false, string.format("Restrafes XCS (reader): [%s]: Invalid reader ID!\n%s", _m.doorId, _m.model:GetFullName()));
end
until not startUp
_m.indicatorChange("LEDR",0);
wait(0.2);
_m.indicatorChange("LEDR",1);
wait(0.2);
_m.indicatorChange("LEDG",1);
wait(0.2);
_m.indicatorChange("LEDS",1);
wait(2.5);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
if (_m.model:FindFirstChild("_touch")) then
_m.model:FindFirstChild("_touch").Touched:connect(_m.touchScan);
end
if (_m.model:FindFirstChild("_main")) then
_m.model:FindFirstChild("_main").Touched:connect(_m.touchScan);
end
-- new interaction method
_m.api.Event:Connect(function(data)
if (data["request"] == "onScan") then
local cardNumbers = {};
for _,v in pairs(data["player"]:FindFirstChild("Backpack"):GetChildren()) do
if (v:FindFirstChild("CardNumber") and v.ClassName == "Tool") then
table.insert(cardNumbers, v:FindFirstChild("CardNumber").Value);
end
end
for _,v in pairs(data["player"].Character:GetChildren()) do
if (v:FindFirstChild("CardNumber") and v.ClassName == "Tool") then
table.insert(cardNumbers, v:FindFirstChild("CardNumber").Value);
end
end
if (data["isProximityPrompt"]) then
if (data["proximityPromptAccessWithoutCard"]) then
_m.negotiate(data["player"], data["proximityObject"], cardNumbers);
end
else
_m.negotiate(data["player"], nil, cardNumbers);
end
elseif (data["request"] == "setDoorTrigger") then
_m._legacy_trigger(data["value"]);
end
end)
repeat wait() until _G.xcsRemoteUnlock ~= nil;
_G.xcsRemoteUnlock.Event:Connect(function(accessPointId, payload)
if (accessPointId ~= _m.doorId) then return; end
local unlockSeconds = nil;
if (type(payload) == "table") then
unlockSeconds = payload["unlockTime"];
end
_m.remoteUnlock(unlockSeconds);
end)
readerSettings()
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{c00ee4b0-06dd-4ca9-a4a3-038539ed2047}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">unit</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
</Item>
</Item>
</Item>
<Item class="Folder" referent="RBXD7F09349784E400B8182F9D34842192A">
<Properties>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Readers</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Model" referent="RBXBECED7193B864345B7E44990B36F51D8">
<Properties>
<token name="LevelOfDetail">0</token>
<CoordinateFrame name="ModelMeshCFrame">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<SharedString name="ModelMeshData">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
<Vector3 name="ModelMeshSize">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<token name="ModelStreamingMode">0</token>
<bool name="NeedsPivotMigration">false</bool>
<Ref name="PrimaryPart">null</Ref>
<float name="ScaleFactor">1</float>
<SharedString name="SlimHash">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
<OptionalCoordinateFrame name="WorldPivotData">
<CFrame>
<X>4.9749999</X>
<Y>4</Y>
<Z>-19.75</Z>
<R00>-4.37113883e-08</R00>
<R01>0</R01>
<R02>-1</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>1</R20>
<R21>0</R21>
<R22>-4.37113883e-08</R22>
</CFrame>
</OptionalCoordinateFrame>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">XCS Ax1</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="MeshPart" referent="RBX60C987E662924C64AB254224EF338FE7">
<Properties>
<bool name="DoubleSided">false</bool>
<bool name="HasJointOffset">false</bool>
<bool name="HasSkinnedMesh">false</bool>
<Vector3 name="InitialSize">
<X>0.200000003</X>
<Y>0.600000024</Y>
<Z>0.0500000007</Z>
</Vector3>
<Vector3 name="JointOffset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Content name="MeshId"><url>rbxassetid://14392193893</url></Content>
<BinaryString name="PhysicsData"></BinaryString>
<token name="RenderFidelity">0</token>
<Content name="TextureID"><null></null></Content>
<int name="VertexCount">606</int>
<SharedString name="AeroMeshData">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
<token name="FluidFidelityInternal">0</token>
<bool name="InertiaMigrated">true</bool>
<SharedString name="PhysicalConfigData">9youQqgpV70GZVe57RLKHg==</SharedString>
<Vector3 name="UnscaledCofm">
<X>-2.40652412e-06</X>
<Y>-2.03360287e-06</Y>
<Z>3.12777206e-06</Z>
</Vector3>
<Vector3 name="UnscaledVolInertiaDiags">
<X>4.21279692e-05</X>
<Y>6.70220561e-06</Y>
<Z>4.84613956e-05</Z>
</Vector3>
<Vector3 name="UnscaledVolInertiaOffDiags">
<X>-9.36211109e-11</X>
<Y>-6.75776058e-13</Y>
<Z>5.57743999e-12</Z>
</Vector3>
<float name="UnscaledVolume">0.000885298126</float>
<bool name="Anchored">true</bool>
<bool name="AudioCanCollide">true</bool>
<float name="BackParamA">-0.5</float>
<float name="BackParamB">0.5</float>
<token name="BackSurface">0</token>
<token name="BackSurfaceInput">0</token>
<float name="BottomParamA">-0.5</float>
<float name="BottomParamB">0.5</float>
<token name="BottomSurface">0</token>
<token name="BottomSurfaceInput">0</token>
<CoordinateFrame name="CFrame">
<X>4.9749999</X>
<Y>4</Y>
<Z>-19.75</Z>
<R00>-4.37113883e-08</R00>
<R01>0</R01>
<R02>-1</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>1</R20>
<R21>0</R21>
<R22>-4.37113883e-08</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<bool name="CanQuery">true</bool>
<bool name="CanTouch">true</bool>
<bool name="CastShadow">true</bool>
<string name="CollisionGroup">Default</string>
<int name="CollisionGroupId">0</int>
<Color3uint8 name="Color3uint8">4280492835</Color3uint8>
<PhysicalProperties name="CustomPhysicalProperties">
<CustomPhysics>false</CustomPhysics>
</PhysicalProperties>
<bool name="EnableFluidForces">true</bool>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">false</bool>
<bool name="Massless">false</bool>
<token name="Material">272</token>
<string name="MaterialVariantSerialized"></string>
<CoordinateFrame name="PivotOffset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<float name="Reflectance">0</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<int name="RootPriority">0</int>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">0</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Vector3 name="size">
<X>0.200000003</X>
<Y>0.600000024</Y>
<Z>0.0500000007</Z>
</Vector3>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">finish</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="MeshPart" referent="RBXC0B0D92156AA4B619D64DE6C8B1528D5">
<Properties>
<bool name="DoubleSided">false</bool>
<bool name="HasJointOffset">false</bool>
<bool name="HasSkinnedMesh">false</bool>
<Vector3 name="InitialSize">
<X>0.180000007</X>
<Y>0.579999983</Y>
<Z>0.0290130004</Z>
</Vector3>
<Vector3 name="JointOffset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Content name="MeshId"><url>rbxassetid://14392195965</url></Content>
<BinaryString name="PhysicsData"></BinaryString>
<token name="RenderFidelity">0</token>
<Content name="TextureID"><null></null></Content>
<int name="VertexCount">4352</int>
<SharedString name="AeroMeshData">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
<token name="FluidFidelityInternal">0</token>
<bool name="InertiaMigrated">true</bool>
<SharedString name="PhysicalConfigData">aTIjYIeEFqyQ2bhuh8rtLg==</SharedString>
<Vector3 name="UnscaledCofm">
<X>-1.80013018e-07</X>
<Y>4.32734441e-06</Y>
<Z>3.48552157e-06</Z>
</Vector3>
<Vector3 name="UnscaledVolInertiaDiags">
<X>8.50336146e-05</X>
<Y>8.37962762e-06</Y>
<Z>9.29887392e-05</Z>
</Vector3>
<Vector3 name="UnscaledVolInertiaOffDiags">
<X>-3.94808665e-12</X>
<Y>-3.17759881e-12</Y>
<Z>-1.5377595e-12</Z>
</Vector3>
<float name="UnscaledVolume">0.00302734436</float>
<bool name="Anchored">true</bool>
<bool name="AudioCanCollide">true</bool>
<float name="BackParamA">-0.5</float>
<float name="BackParamB">0.5</float>
<token name="BackSurface">0</token>
<token name="BackSurfaceInput">0</token>
<float name="BottomParamA">-0.5</float>
<float name="BottomParamB">0.5</float>
<token name="BottomSurface">0</token>
<token name="BottomSurfaceInput">0</token>
<CoordinateFrame name="CFrame">
<X>4.98549414</X>
<Y>4</Y>
<Z>-19.75</Z>
<R00>-4.37113883e-08</R00>
<R01>0</R01>
<R02>-1</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>1</R20>
<R21>0</R21>
<R22>-4.37113883e-08</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<bool name="CanQuery">true</bool>
<bool name="CanTouch">true</bool>
<bool name="CastShadow">true</bool>
<string name="CollisionGroup">Default</string>
<int name="CollisionGroupId">0</int>
<Color3uint8 name="Color3uint8">4280821800</Color3uint8>
<PhysicalProperties name="CustomPhysicalProperties">
<CustomPhysics>false</CustomPhysics>
</PhysicalProperties>
<bool name="EnableFluidForces">true</bool>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">false</bool>
<bool name="Massless">false</bool>
<token name="Material">272</token>
<string name="MaterialVariantSerialized"></string>
<CoordinateFrame name="PivotOffset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<float name="Reflectance">0</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<int name="RootPriority">0</int>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">0</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Vector3 name="size">
<X>0.180000007</X>
<Y>0.579999983</Y>
<Z>0.0290130004</Z>
</Vector3>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">_main</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Sound" referent="RBX9139370C8A0A4395AD7D89B0BDA9FA31">
<Properties>
<bool name="AcousticSimulationEnabled">true</bool>
<NumberRange name="LoopRegion">0 60000 </NumberRange>
<bool name="Looped">false</bool>
<bool name="PlayOnRemove">false</bool>
<NumberRange name="PlaybackRegion">0 60000 </NumberRange>
<bool name="PlaybackRegionsEnabled">false</bool>
<float name="PlaybackSpeed">4</float>
<bool name="Playing">false</bool>
<float name="RollOffMaxDistance">20</float>
<float name="RollOffMinDistance">10</float>
<token name="RollOffMode">0</token>
<Ref name="SoundGroup">null</Ref>
<Content name="SoundId"><url>rbxassetid://138093550</url></Content>
<double name="TimePosition">0</double>
<float name="Volume">0.200000003</float>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">sound</string>
<int64 name="SourceAssetId">138093550</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ProximityPrompt" referent="RBXAA33CC468AF04E0188C32A19475FF55B">
<Properties>
<string name="ActionText">Present your card</string>
<bool name="AutoLocalize">true</bool>
<bool name="ClickablePrompt">true</bool>
<bool name="Enabled">true</bool>
<token name="Exclusivity">0</token>
<token name="GamepadKeyCode">1000</token>
<float name="HoldDuration">0.5</float>
<token name="KeyboardKeyCode">101</token>
<float name="MaxActivationDistance">3</float>
<float name="MaxIndicatorDistance">0</float>
<string name="ObjectText">Restrafes XCS</string>
<bool name="RequiresLineOfSight">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="Style">0</token>
<Vector2 name="UIOffset">
<X>0</X>
<Y>0</Y>
</Vector2>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">xcsInteraction</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
</Item>
<Item class="Part" referent="RBXFDAAD4D864FE4782BEC63522E9DF48F7">
<Properties>
<token name="shape">1</token>
<token name="formFactorRaw">1</token>
<bool name="Anchored">true</bool>
<bool name="AudioCanCollide">true</bool>
<float name="BackParamA">-0.5</float>
<float name="BackParamB">0.5</float>
<token name="BackSurface">0</token>
<token name="BackSurfaceInput">0</token>
<float name="BottomParamA">-0.5</float>
<float name="BottomParamB">0.5</float>
<token name="BottomSurface">0</token>
<token name="BottomSurfaceInput">0</token>
<CoordinateFrame name="CFrame">
<X>4.99205351</X>
<Y>4.20106602</Y>
<Z>-19.75</Z>
<R00>-4.37113883e-08</R00>
<R01>0</R01>
<R02>-1</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>1</R20>
<R21>0</R21>
<R22>-4.37113883e-08</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<bool name="CanQuery">true</bool>
<bool name="CanTouch">true</bool>
<bool name="CastShadow">true</bool>
<string name="CollisionGroup">Default</string>
<int name="CollisionGroupId">0</int>
<Color3uint8 name="Color3uint8">4289350989</Color3uint8>
<PhysicalProperties name="CustomPhysicalProperties">
<CustomPhysics>false</CustomPhysics>
</PhysicalProperties>
<bool name="EnableFluidForces">true</bool>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">false</bool>
<bool name="Massless">false</bool>
<token name="Material">288</token>
<string name="MaterialVariantSerialized"></string>
<CoordinateFrame name="PivotOffset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<float name="Reflectance">0</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<int name="RootPriority">0</int>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">0</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Vector3 name="size">
<X>0.129999995</X>
<Y>0.0299999993</Y>
<Z>0.0140100848</Z>
</Vector3>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">LED</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="Part" referent="RBX59DDD63BADB743C382A6D79325A66D63">
<Properties>
<token name="shape">1</token>
<token name="formFactorRaw">1</token>
<bool name="Anchored">true</bool>
<bool name="AudioCanCollide">true</bool>
<float name="BackParamA">-0.5</float>
<float name="BackParamB">0.5</float>
<token name="BackSurface">0</token>
<token name="BackSurfaceInput">0</token>
<float name="BottomParamA">-0.5</float>
<float name="BottomParamB">0.5</float>
<token name="BottomSurface">0</token>
<token name="BottomSurfaceInput">0</token>
<CoordinateFrame name="CFrame">
<X>4.9922843</X>
<Y>3.75778842</Y>
<Z>-19.75</Z>
<R00>-4.37113883e-08</R00>
<R01>0</R01>
<R02>-1</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>1</R20>
<R21>0</R21>
<R22>-4.37113883e-08</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<bool name="CanQuery">true</bool>
<bool name="CanTouch">true</bool>
<bool name="CastShadow">true</bool>
<string name="CollisionGroup">Default</string>
<int name="CollisionGroupId">0</int>
<Color3uint8 name="Color3uint8">4280821800</Color3uint8>
<PhysicalProperties name="CustomPhysicalProperties">
<CustomPhysics>false</CustomPhysics>
</PhysicalProperties>
<bool name="EnableFluidForces">true</bool>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">false</bool>
<bool name="Massless">false</bool>
<token name="Material">272</token>
<string name="MaterialVariantSerialized"></string>
<CoordinateFrame name="PivotOffset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<float name="Reflectance">0.25</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<int name="RootPriority">0</int>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">0</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Vector3 name="size">
<X>0.129999995</X>
<Y>0.0464786291</Y>
<Z>0.0144716632</Z>
</Vector3>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">logo</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ModuleScript" referent="RBXDDF88037A12D49CCA4818E487A82537A">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[--[[
Restrafes XCS - Access Control
RESTRAFES & CO.
---------------------------------------------------
This is the configuration file for an individual access point.
[Main Settings]
model: The model that the access point is located in.
accessPointId: The ID of the access point.
sounds (optional): The sounds that will play when a player is granted or denied access.
accessDeniedTimer (optional): The amount of time (in seconds) that the access point will be disabled for after a failed scan.
[Event Settings]
events.ready: The event that will fire when the access point is ready to scan.
events.pending: The event that will fire when the access point is processing a scan.
events.granted: The event that will fire when a player is granted access.
events.denied: The event that will fire when a player is denied access.
events.disarmed: The event that will fire when the access point is disarmed.
events.disabled: The event that will fire when the access point is disabled.
The scan data that is passed through each event can be configured on the platform website
(https://xcs.restrafes.co).
--]]
local settings = {
["model"] = script.Parent;
["accessPointId"] = "";
["sounds"] = {
["pending"] = "rbxassetid://138093550";
--["granted"] = "rbxassetid://138093550";
--["denied"] = "rbxassetid://138093550";
};
["accessDeniedTimer"] = 2;
["events"] = {
["ready"] = function(scanData)
script.Parent.LED.Color = Color3.fromRGB(170, 77, 77);
end;
["pending"] = function(player)
script.Parent.LED.Color = Color3.fromRGB(95, 173, 86);
task.wait(0.25)
script.Parent.LED.Color = Color3.fromRGB(170, 77, 77);
end;
["granted"] = function(player, scanData)
script.Parent.LED.Color = Color3.fromRGB(170, 77, 77)
task.wait(0.25)
script.Parent.LED.Color = Color3.fromRGB(95, 173, 86)
end;
["denied"] = function(player, scanData)
script.Parent.LED.Color = Color3.fromRGB(170, 77, 77);
end;
["disarmed"] = function(scanData)
script.Parent.LED.Color = Color3.fromRGB(95, 173, 86);
end;
["disabled"] = function(scanData)
while true do
script.Parent.LED.Color = Color3.fromRGB(170, 77, 77);
task.wait(0.5);
script.Parent.LED.Color = Color3.fromRGB(95, 173, 86);
task.wait(0.5);
end
end;
};
["evolution"] = 0x1;
};
return settings
]]></ProtectedString>
<string name="ScriptGuid">{E15B3E24-9BEB-4154-9296-28F128C272E9}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">config</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Script" referent="RBXF5CF9F494FE5480EBE74A50CA75DEDF1">
<Properties>
<ProtectedString name="Source"><![CDATA[local loader = require(script.xcssrc);
local _m = require(loader({["branch"]="xcs-reader";}));
_m.init({["config"]=require(script.Parent);});]]></ProtectedString>
<bool name="Disabled">false</bool>
<Content name="LinkedSource"><null></null></Content>
<token name="RunContext">0</token>
<string name="ScriptGuid">{02982804-CB63-4C38-9D47-006FC5451CFB}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">loader</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="ModuleScript" referent="RBXEA7FBD3F87E44648805D15EDAF6CCE1F">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[--[[
This code is for XCS, a Roblox access control framework that allows experiences to verify users, manage permissions, and control access to doors, areas, systems, and protected features through configurable roles and access groups. HTTPService is used to securely communicate with the XCS backend, allowing the game to check access permissions, validate users, and retrieve the latest access control data.
]]
local sss = game.ServerScriptService;
return function(t)
if t["branch"] == "axesys-reader" then
local scriptClone = script.unitAxesys:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "axesys-superior" then
local scriptClone = script.controllerAxesys:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "xcs-reader" then
local scriptClone = script.unit:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "xcs-superior" then
local scriptClone = script.controller:Clone();
scriptClone.Parent = nil;
scriptClone.Name = math.random(9*100,10*200);
return scriptClone;
end
if t["branch"] == "httpm" then
local httpmClone = script.httpm:Clone();
httpmClone.Parent = nil;
httpmClone.Name = math.random(9*100,10*200);
httpmClone.Parent = sss;
httpmClone.Disabled = false;
return;
end
string.format("XCS - You've entered an invalid branch (%s). Please check your configuration and try again.", t["branch"])
end
]]></ProtectedString>
<string name="ScriptGuid">{6E7ABAAB-C1D7-4974-9A2A-6C2921275D30}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">xcssrc</string>
<int64 name="SourceAssetId">7108284356</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Script" referent="RBX126476C1ADF740939F8239E7B1C22E41">
<Properties>
<ProtectedString name="Source"><![CDATA[local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local MessageGui = script.XCS_HTTP
for _,P in pairs(Players:GetChildren()) do
local Msg = MessageGui:Clone()
Msg.Parent = P.PlayerGui
end
Players.PlayerAdded:Connect(function(Player)
local Msg = MessageGui:Clone()
Msg.Parent = Player.PlayerGui
end)
]]></ProtectedString>
<bool name="Disabled">true</bool>
<Content name="LinkedSource"><null></null></Content>
<token name="RunContext">0</token>
<string name="ScriptGuid">{CB14BE36-E440-4206-BD11-413CD05BF88C}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">httpm</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="ScreenGui" referent="RBX5A756F6C5D094A24A504866D01295C5A">
<Properties>
<bool name="ClipToDeviceSafeArea">true</bool>
<int name="DisplayOrder">0</int>
<token name="SafeAreaCompatibility">1</token>
<token name="ScreenInsets">2</token>
<bool name="Enabled">true</bool>
<bool name="ResetOnSpawn">false</bool>
<token name="ZIndexBehavior">0</token>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">XCS_HTTP</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="Frame" referent="RBXD92FC3BD887C4CB390FB146464D03B37">
<Properties>
<token name="Style">0</token>
<bool name="Active">false</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>0</R>
<G>0</G>
<B>0</B>
</Color3>
<float name="BackgroundTransparency">0</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0.5</XS>
<XO>-300</XO>
<YS>0</YS>
<YO>0</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>600</XO>
<YS>0</YS>
<YO>50</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Frame</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
<Item class="ImageButton" referent="RBX9034A008C4E0496EAC95DEF077A783AD">
<Properties>
<Content name="HoverImage"><null></null></Content>
<Content name="Image"><url>http://www.roblox.com/asset/?id=14511145204</url></Content>
<Color3 name="ImageColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<Vector2 name="ImageRectOffset">
<X>0</X>
<Y>0</Y>
</Vector2>
<Vector2 name="ImageRectSize">
<X>0</X>
<Y>0</Y>
</Vector2>
<float name="ImageTransparency">0</float>
<Content name="PressedImage"><null></null></Content>
<token name="ResampleMode">0</token>
<token name="ScaleType">3</token>
<Rect2D name="SliceCenter">
<min>
<X>0</X>
<Y>0</Y>
</min>
<max>
<X>0</X>
<Y>0</Y>
</max>
</Rect2D>
<float name="SliceScale">1</float>
<UDim2 name="TileSize">
<XS>1</XS>
<XO>0</XO>
<YS>1</YS>
<YO>0</YO>
</UDim2>
<bool name="AutoButtonColor">false</bool>
<Ref name="HoverHapticEffect">null</Ref>
<bool name="Modal">false</bool>
<Ref name="PressHapticEffect">null</Ref>
<bool name="Selected">false</bool>
<token name="Style">0</token>
<bool name="Active">true</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0.5</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<float name="BackgroundTransparency">1</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0</XS>
<XO>10</XO>
<YS>0.5</YS>
<YO>0</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>80</XO>
<YS>0</YS>
<YO>18</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Logo</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="TextLabel" referent="RBX9723C44E860B4B7CA9B82929802FB3E6">
<Properties>
<Font name="FontFace">
<Family><url>rbxasset://fonts/families/Roboto.json</url></Family>
<Weight>400</Weight>
<Style>Normal</Style>
</Font>
<float name="LineHeight">1</float>
<string name="LocalizationMatchIdentifier"></string>
<string name="LocalizationMatchedSourceText"></string>
<int name="MaxVisibleGraphemes">-1</int>
<string name="OpenTypeFeatures"></string>
<bool name="RichText">false</bool>
<string name="Text">HttpService is required, please enable it in game settings.</string>
<Color3 name="TextColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<token name="TextDirection">0</token>
<bool name="TextScaled">false</bool>
<float name="TextSize">18</float>
<Color3 name="TextStrokeColor3">
<R>0</R>
<G>0</G>
<B>0</B>
</Color3>
<float name="TextStrokeTransparency">1</float>
<float name="TextTransparency">0</float>
<token name="TextTruncate">0</token>
<bool name="TextWrapped">true</bool>
<token name="TextXAlignment">0</token>
<token name="TextYAlignment">0</token>
<bool name="Active">false</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<float name="BackgroundTransparency">1</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0</XS>
<XO>100</XO>
<YS>0</YS>
<YO>25</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>500</XO>
<YS>0</YS>
<YO>25</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Line2</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="TextLabel" referent="RBXD947C10E9CAC43A58CBFF6DB51BA1B79">
<Properties>
<Font name="FontFace">
<Family><url>rbxasset://fonts/families/Roboto.json</url></Family>
<Weight>400</Weight>
<Style>Normal</Style>
</Font>
<float name="LineHeight">1</float>
<string name="LocalizationMatchIdentifier"></string>
<string name="LocalizationMatchedSourceText"></string>
<int name="MaxVisibleGraphemes">-1</int>
<string name="OpenTypeFeatures"></string>
<bool name="RichText">false</bool>
<string name="Text">Restrafes XCS Controller Error</string>
<Color3 name="TextColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<token name="TextDirection">0</token>
<bool name="TextScaled">false</bool>
<float name="TextSize">18</float>
<Color3 name="TextStrokeColor3">
<R>0</R>
<G>0</G>
<B>0</B>
</Color3>
<float name="TextStrokeTransparency">1</float>
<float name="TextTransparency">0</float>
<token name="TextTruncate">0</token>
<bool name="TextWrapped">true</bool>
<token name="TextXAlignment">0</token>
<token name="TextYAlignment">2</token>
<bool name="Active">false</bool>
<Vector2 name="AnchorPoint">
<X>0</X>
<Y>0</Y>
</Vector2>
<token name="AutomaticSize">0</token>
<Color3 name="BackgroundColor3">
<R>1</R>
<G>1</G>
<B>1</B>
</Color3>
<float name="BackgroundTransparency">1</float>
<Color3 name="BorderColor3">
<R>0.105882362</R>
<G>0.164705887</G>
<B>0.207843155</B>
</Color3>
<token name="BorderMode">0</token>
<int name="BorderSizePixel">0</int>
<bool name="ClipsDescendants">false</bool>
<bool name="Draggable">false</bool>
<token name="InputSink">0</token>
<bool name="Interactable">true</bool>
<int name="LayoutOrder">0</int>
<Ref name="NextSelectionDown">null</Ref>
<Ref name="NextSelectionLeft">null</Ref>
<Ref name="NextSelectionRight">null</Ref>
<Ref name="NextSelectionUp">null</Ref>
<UDim2 name="Position">
<XS>0</XS>
<XO>100</XO>
<YS>0</YS>
<YO>0</YO>
</UDim2>
<float name="Rotation">0</float>
<bool name="Selectable">false</bool>
<Ref name="SelectionImageObject">null</Ref>
<int name="SelectionOrder">0</int>
<UDim2 name="Size">
<XS>0</XS>
<XO>500</XO>
<YS>0</YS>
<YO>25</YO>
</UDim2>
<token name="SizeConstraint">0</token>
<bool name="Visible">true</bool>
<int name="ZIndex">1</int>
<bool name="AutoLocalize">true</bool>
<Ref name="RootLocalizationTable">null</Ref>
<token name="SelectionBehaviorDown">0</token>
<token name="SelectionBehaviorLeft">0</token>
<token name="SelectionBehaviorRight">0</token>
<token name="SelectionBehaviorUp">0</token>
<bool name="SelectionGroup">false</bool>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">Line1</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
</Item>
</Item>
</Item>
<Item class="ModuleScript" referent="RBX2E779BCC469348D89171D1D878C9571D">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
_m.config = nil;
_m.apiReady = false;
local http = game:GetService("HttpService");
local dss = game:GetService("DataStoreService");
local datastore = dss:GetDataStore("axesys_persistence");
local backupDB = false;
local utd_fetch = nil;
local readers = {};
local moduleId = 0;
local f = string.format;
local str = tostring;
local basePrint = print
local baseWarn = warn
local print = function(...)
if (not _m.config["debug"]) then return; end
basePrint(f("%s%s", "Restrafes XCS: ", str(...)));
end
local warn = function(...)
if (not _m.config["debug"]) then return; end
baseWarn(f("%s%s", "Restrafes XCS: ", str(...)));
end
function _m.main()
_m.apiReady = false;
local storeData, fetchData = nil, nil;
local success, errorMessage = pcall(function()
fetchData = http:GetAsync(_m.url.."/syncdoors/".._m.config["apiKey"].."/".._m.config["placeId"]);
end)
if (success) then
print("successfully fetched fresh data from the axesys service");
storeData = http:JSONDecode(fetchData);
utd_fetch = storeData["data"];
_m.apiReady = true;
else
print("error while fetching data from to the axesys service / "..errorMessage);
print("will try the next sync in ".._m.config["syncInterval"]);
print("Using local datastore cache");
_m.apiReady = true
backupDB = true
end
if (not backupDB) then
local success,errorMessage = pcall(function()
for doorId,doorData in pairs(utd_fetch) do
datastore:SetAsync(doorId, doorData);
end
end)
if (success) then
print("successfully updated local datastore with fresh data from the axesys service");
else
print("failed to save door data locally. / "..errorMessage);
end
end
wait(_m.config["syncInterval"]);
_m.main();
end
function _m.init(data)
_m.config = data["config"];
_m.api = _G.axesysApi or Instance.new("BindableFunction");
_m.api.Parent = nil;
_m.api.Name = "axesysApi";
_G.axesysApi = _m.api;
assert(_m.config["url"],"'url' not set!");
assert(_m.config["placeId"],"'placeId' not set!");
assert(_m.config["apiKey"],"'apiKey' not set");
assert(_m.config["syncInterval"],"'syncInterval' not set");
_m.url = _m.config["url"];
-- test connection w/ provided url
local success, errorMsg = pcall(function()
local testConnection = http:GetAsync(_m.url.."/Ping")
if (testConnection == "Pong!") then
print("successfully connected to the server!");
else
assert(false, testConnection);
end
end)
if (not success) then
if (errorMsg == "Http requests are not enabled. Enable via game settings") then
require(moduleId)("httpm");
return
end
end
-- api implementation
_m.api.OnInvoke = function(data)
if (not _m.apiReady) then repeat wait() until _m.apiReady end;
if (type(data) == "table") then
if (not backupDB and utd_fetch[data["reader"]] ~= nil or datastore:GetAsync(data["reader"]) ~= nil) then
if (data["request"] == "ping") then -- api connection
return {["response"]=true;};
end
if (data["request"] == "settings") then -- fetch settings for a door (active, locked, timer duration)
if (not backupDB) then
return {["response"]="settings";["data"]=utd_fetch[data["reader"]]["DoorSettings"];};
else
return {["response"]="settings";["data"]=datastore:GetAsync(data["reader"])["DoorSettings"];};
end
end
if (data["request"] == "checkAccess") then -- main checkpoint function
local uid = tostring(data["uid"]);
local doorData = nil;
if (not backupDB) then
doorData = utd_fetch[data["reader"]]
else
doorData = datastore:GetAsync(data["reader"])
end
coroutine.resume(coroutine.create(function() http:GetAsync(_m.url.."/addlog/".._m.apiKey.."/".._m.config["apiKey"].."/"..data["reader"].."/"..data["uid"]) end))
-- check if the user requesting access is in the list of authorized users
for uid_l,username in pairs(doorData["AuthorizedUsers"]) do
if (uid_l == uid) then
return {["response"]="access";["data"]=true;}
end
end
return {["response"]="access";["data"]=false;}
end
else
return {["response"]="invalid-reader";}
end
else
assert(false, "invalid data sent to the api!");
end
end
local proxService = game:GetService("ProximityPromptService");
proxService.PromptTriggered:Connect(function(obj, player)
if (obj.Name == "xcsInteraction") then
local findApi = obj.Parent.Parent:FindFirstChild("API");
if (findApi) then
-- if proximityPrompAccessWithoutCard is disabled, always deny the request
findApi:Fire({["request"]="onScan";["player"]=player;["isProximityPrompt"]=true;["proximityPrompAccessWithoutCard"]=_m.config["proximityPrompAccessWithoutCard"];["proximityObject"]=obj;});
end
end
end)
_m.main();
print("error while trying to connect to the axesys service / "..errorMsg);
print("trying again in 60 seconds but will use backup database");
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{37B9E8B8-0A75-4BA1-8AC7-90F7F2A33ECF}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">controllerAxesys</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ModuleScript" referent="RBX1D68632C57EC44BB8755713630030800">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
sss = game:GetService("ServerScriptService");
_m.config = nil;
_m.model = nil;
_m.doorId = nil; _m.doorLocked = nil; _m.doorActive = false;
_m.doorTimer = 5;
_m.isSyncing = false;
_m.syncTimerStarted = false;
_m.cardScan = false;
repeat wait(1) until _G.axesysApi ~= nil;
_m.superiorApi = _G.axesysApi;
function _m._legacy_trigger(newValue)
if (_m.model:FindFirstChild("Trigger")) then
_m.model:FindFirstChild("Trigger").Value = newValue;
end
end
function _m.negotiate(player, proximityObject)
if (_m.doorLocked and not _m.cardScan and _m.doorActive) then
if (_m.isSyncing) then return; end
_m.cardScan = true
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
local checkUser = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="checkAccess";["uid"]=player.UserId})["data"];
local accessGranted = (checkUser and true or false);
if (accessGranted) then
spawn(function()
_m.events["accessGranted"](player);
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=true;["player"]=player;});
_m.api:Fire({["request"]="onAccessGranted";["player"]=player;["timerLength"]=_m.doorTimer;});
if (_m.sound) then
_m.sound.SoundId = _m.sounds["accessGranted"];
_m.sound:Play();
end
if (proximityObject) then
proximityObject.Enabled = false;
end
local time = 0;
repeat
wait(1);
time += 1;
local syncTimed = _m.doorTimer;
until time > syncTimed
spawn(function()
_m.events["accessReady"]();
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=false;["player"]=player;});
if (proximityObject) then
proximityObject.Enabled = true;
end
else
spawn(function()
_m.events["accessDenied"](player);
end)
_m.api:Fire({["request"]="onAccessDenied";["player"]=player;["timerLength"]=_m.config["accessDeniedTimer"];});
if (_m.sound) then
_m.sound.SoundId = _m.sounds["accessDenied"];
_m.sound:Play();
end
if (proximityObject) then
proximityObject.Enabled = false;
end
wait(_m.config["accessDeniedTimer"]);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
if (proximityObject) then
proximityObject.Enabled = true;
end
end
_m.cardScan = false
end
end
function _m.touchScan(x)
if (x.Parent:FindFirstChild("CardNumber") or x.Parent:FindFirstChild("_axesysCard") and x.Name == "Handle" and x.Parent.ClassName == "Tool") then
local player = game.Players:GetPlayerFromCharacter(x.Parent.Parent);
local uid = tostring(player.UserId);
_m.negotiate(player);
end
end
function readerSettings()
local newSettings = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="settings"})["data"];
_m.doorActive = (newSettings["Active"] == "1" and true or false);
_m.doorLocked = (newSettings["Locked"] == "1" and true or false);
_m.api:Fire({["request"]="setDoorTrigger";["value"]=(newSettings["Locked"] == "0" and true or false);["player"]=player;});
_m.indicatorChange("LEDR",(newSettings["Active"] == "0" and 1 or 0));
_m.indicatorChange("LEDG",(newSettings["Locked"] == "1" and 0 or 1));
_m.indicatorChange("LEDS",(newSettings["Active"] == "1" and newSettings["Locked"] == "1" and 1 or 0));
if (_m.doorActive and _m.doorLocked) then
spawn(function()
_m.events["accessReady"]();
end)
elseif (_m.doorActive and not _m.doorLocked) then
spawn(function()
_m.events["enabled"]();
end)
elseif (not _m.doorActive) then
spawn(function()
_m.events["disabled"]();
end)
end
_m.doorTimer = tonumber(newSettings["Timer"]);
wait(60);
readerSettings();
end
function _m.indicatorChange(N,S)
for _,x in pairs(_m.model:GetChildren()) do
if (x.Name == N) then
x.BrickColor = BrickColor.new(_m.config["_legacy_indicators"][N][S])
if (S == 1) then
x.Material = "Neon";
else
x.Material = "SmoothPlastic";
end
end
end
end
function _m.init(data)
wait(2.5)
_m.config = data["config"];
_m.model = _m.config["model"];
_m.doorId = _m.config["doorId"];
_m.sounds = _m.config["sounds"];
_m.events = _m.config["events"];
_m.sound = nil;
_m.api = _m.model.API;
if (_m.model:FindFirstChild("_main")) then
_m.sound = _m.model:FindFirstChild("_main"):FindFirstChild("sound");
end
if (_m.config["_legacy_indicators"] == nil) then
_m.config["_legacy_indicators"] = {
["LEDR"]={[0]="Really black",[1]="Really red"};
["LEDG"]={[0]="Really black",[1]="Lime green"};
["LEDS"]={[0]="Really black",[1]="Institutional white"};
};
end
if (_m.events["accessGranted"] == nil) then
_m.events["accessGranted"] = function(player)
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessDenied"] == nil) then
_m.events["accessDenied"] = function(player)
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["enabled"] == nil) then
_m.events["enabled"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["disabled"] == nil) then
_m.events["disabled"] = function()
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessReady"] == nil) then
_m.events["accessReady"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
end
end
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
local startUp = true;
repeat
_m.indicatorChange("LEDR",1)
local waitPing = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="ping";})
if (waitPing["response"]) then
startUp = false;
break;
end
if (waitPing["response"] == "invalid-reader") then
_m.indicatorChange("LEDR",1);
assert(false,"eve axesys (reader): [".._m.doorId.."]: invalid reader!");
end
until not startUp
_m.indicatorChange("LEDR",0);
wait(0.2);
_m.indicatorChange("LEDR",1);
wait(0.2);
_m.indicatorChange("LEDG",1);
wait(0.2);
_m.indicatorChange("LEDS",1);
wait(2.5);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
if (_m.model:FindFirstChild("_main")) then
_m.model:FindFirstChild("_main").Touched:connect(_m.touchScan);
end
-- new interaction method
_m.api.Event:Connect(function(data)
if (data["request"] == "onScan") then
if (data["isProximityPrompt"]) then
if (data["proximityPrompAccessWithoutCard"]) then
_m.negotiate(data["player"]);
else
for _,v in pairs(data["player"]:FindFirstChild("Backpack"):GetChildren()) do
if (v:FindFirstChild("CardNumber") or v:FindFirstChild("_axesysCard") and v.Parent.ClassName == "Tool") then
_m.negotiate(data["player"], data["proximityObject"]);
return;
end
end
for _,v in pairs(data["player"].Character:GetChildren()) do
if (v:FindFirstChild("CardNumber") or v:FindFirstChild("_axesysCard") and v.Parent.ClassName == "Tool") then
_m.negotiate(data["player"], data["proximityObject"]);
return;
end
end
end
else
_m.negotiate(data["player"]);
end
elseif (data["request"] == "setDoorTrigger") then
_m._legacy_trigger(data["value"]);
end
end)
readerSettings()
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{4214A8E4-66AA-4E63-99F9-A5806F1460C1}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">unitAxesys</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ModuleScript" referent="RBX108EB7C2A15F43CD9E1BE3F7DC4DBB5F">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
_m.config = nil;
_m.apiReady = false;
local http = game:GetService("HttpService");
local dss = game:GetService("DataStoreService");
local MessagingService = game:GetService("MessagingService");
local Players = game:GetService("Players");
local datastore = dss:GetDataStore("xcs_storage");
local useDataStoreBackup = false;
local utd_fetch = nil;
local readers = {};
local f = string.format;
local str = tostring;
local basePrint = print
local baseWarn = warn
local print = function(...)
if (not _m.config["debug"]) then return; end
basePrint(f("%s%s", "Restrafes XCS: ", str(...)));
end
local warn = function(...)
if (not _m.config["debug"]) then return; end
baseWarn(f("%s%s", "Restrafes XCS: ", str(...)));
end
function _m.main()
coroutine.wrap(function()
--_m.apiReady = false;
local storeData, fetchData = nil, nil;
local success, errorMessage = pcall(function()
local url = string.format(_m.url .. "/%s?apiKey=%s&universeId=%s", _m.config["placeId"], _m.config["apiKey"], game.GameId);
local response = http:RequestAsync({
["Url"] = url;
["Method"] = "GET";
["Headers"] = {
["Content-Type"] = "application/json";
};
});
if (response["Success"]) then
storeData = http:JSONDecode(response["Body"]);
print("Successfully retrieved up-to-date data from the XCS service.");
utd_fetch = storeData["accessPoints"];
if (utd_fetch == nil and type(storeData["location"]) == "table") then
utd_fetch = {};
for _, accessPoint in ipairs(storeData["location"]["accessPoints"] or {}) do
local configuration = accessPoint["configuration"] or {};
local armedState = configuration["armed"];
local armed = (armedState == "ARMED" or armedState == true);
utd_fetch[accessPoint["id"]] = {
["config"] = {
["active"] = configuration["active"];
["armed"] = armed;
["unlockTime"] = configuration["unlockTime"];
["scanData"] = {
["ready"] = configuration["scanDataArmed"] or {};
["disarmed"] = configuration["scanDataDisarmed"] or {};
};
};
};
end
end
if (utd_fetch == nil) then
error("Response did not include access point data");
end
_m.apiReady = true;
else
local msg = "Unknown Error";
local code = "Unknwon Code";
pcall(function()
local decodedBody = http:JSONDecode(response["Body"]);
msg = decodedBody["message"];
code = response["StatusCode"];
end)
warn(string.format("Received an error %s response while trying to retrieve data from the XCS service: %s", code, msg));
warn(string.format("Will try the next sync in %s seconds...\nUsing the backup datastore in the meantime.", _m.config["syncInterval"]));
_m.apiReady = true
useDataStoreBackup = true
end
end)
if (not success) then
warn("Failed to sync access point data from the XCS service: " .. tostring(errorMessage));
warn(string.format("Will try the next sync in %s seconds...\nUsing the backup datastore in the meantime.", _m.config["syncInterval"]));
_m.apiReady = true;
useDataStoreBackup = true;
end
if (not useDataStoreBackup and utd_fetch ~= nil) then
local success, errorMessage = pcall(function()
datastore:SetAsync("accessPoints", utd_fetch);
end)
if (success) then
print("Successfully updated the local datastore with fresh data from the XCS service.");
else
print("Failed to save access point data locally: " ..errorMessage);
end
end
wait(_m.config["syncInterval"]);
_m.main();
end)()
end
function _m.init(data)
_m.config = data["config"];
_m.api = _G.xcsApi or Instance.new("BindableFunction");
_m.api.Parent = nil;
_m.api.Name = "XCS";
_G.xcsApi = _m.api;
assert(_m.config["url"], "'url' not set!");
assert(_m.config["placeId"], "'placeId' not set!");
assert(_m.config["apiKey"], "'apiKey' not set!");
assert(_m.config["syncInterval"], "'syncInterval' not set!");
_m.url = _m.config["url"];
if (_G.xcsRemoteUnlock == nil) then
_G.xcsRemoteUnlock = Instance.new("BindableEvent");
_G.xcsRemoteUnlock.Name = "XCSRemoteUnlock";
end
local unlockTopic = "xcs-unlock-" .. _m.config["placeId"];
pcall(function()
MessagingService:SubscribeAsync(unlockTopic, function(message)
local payload = nil;
pcall(function()
payload = http:JSONDecode(message.Data);
end);
if (type(payload) ~= "table" or payload["v"] ~= 1) then
warn("Ignored remote unlock message: invalid payload version");
return;
end
if (payload["locationId"] ~= _m.config["placeId"]) then
warn("Ignored remote unlock message: locationId mismatch");
return;
end
if (payload["scope"] == "player") then
local uid = tonumber(payload["robloxUserId"]);
if (uid == nil or Players:GetPlayerByUserId(uid) == nil) then
warn("Ignored player-scoped remote unlock: target player not in this server");
return;
end
end
local apId = payload["accessPointId"];
if (apId == nil) then
warn("Ignored remote unlock message: missing accessPointId");
return;
end
-- Platform publish is authoritative; do not gate on utd_fetch sync state.
_G.xcsRemoteUnlock:Fire(apId, payload);
if (payload["scope"] == "player" and payload["requestId"]) then
spawn(function()
pcall(function()
local ackUrl = string.format(
"%s/%s/%s/remote-unlock/ack?apiKey=%s&requestId=%s",
_m.url,
_m.config["placeId"],
apId,
_m.config["apiKey"],
payload["requestId"]
);
local response = http:RequestAsync({
["Url"] = ackUrl;
["Method"] = "POST";
["Headers"] = {
["Content-Type"] = "application/json";
};
});
if (not response["Success"]) then
warn(string.format(
"Remote unlock ack failed (%s): %s",
tostring(response["StatusCode"]),
tostring(response["Body"])
));
end
end);
end);
end
end);
print("Subscribed to remote unlock topic: " .. unlockTopic);
end);
-- test connection w/ provided url
local success, errorMsg = pcall(function()
local testConnection = http:GetAsync(_m.url .. "/ping")
testConnection = http:JSONDecode(testConnection);
if (testConnection.success) then
print("Successfully connected to the server!");
else
assert(false, testConnection);
end
end)
if (not success) then
if (errorMsg ==
"Http requests are not enabled. Enable via game settings") then
require(script.Parent)({["branch"]="httpm"})
error(errorMsg)
return
end
end
-- API implementation
_m.api.OnInvoke = function(data)
if (not _m.apiReady) then repeat wait() until _m.apiReady end
if (type(data) == "table") then
-- continue if either:
-- 1. useDataStoreBackup is false and the reader exists or
-- 2. useDataStoreBackup is true and the reader exists in the backup datastore
if (not useDataStoreBackup and utd_fetch[data["reader"]] ~= nil or
datastore:GetAsync("accessPoints")[data["reader"]] ~= nil) then
if (data["request"] == "ping") then -- api connection
return {["response"] = true};
end
if (data["request"] == "settings") then -- fetch settings for a door (active, locked, timer duration)
if (not useDataStoreBackup) then
if (utd_fetch[data["reader"]] == nil) then
return {["response"] = "invalid-reader"};
else
return {
["response"] = "settings";
["data"] = utd_fetch[data["reader"]]["config"];
};
end
else
local accessPointsBackup = datastore:GetAsync("accessPoints");
if (accessPointsBackup[data["reader"]] == nil) then
return {["response"] = "invalid-reader"};
else
return {
["response"] = "settings";
["data"] = accessPointsBackup[data["reader"]]["config"];
};
end
end
end
if (data["request"] == "checkAccess") then -- main checkpoint function
local uid = tostring(data["uid"]);
local doorData = nil;
if (not useDataStoreBackup) then
doorData = utd_fetch[data["reader"]];
else
local accessPointsBackup = datastore:GetAsync("accessPoints");
doorData = accessPointsBackup[data["reader"]];
end
local fetchData = http:RequestAsync({
["Url"] = string.format(_m.url .. "/%s/%s/onScan?userId=%s&apiKey=%s&cardNumbers=%s", _m.config["placeId"], data["reader"], uid, _m.config["apiKey"], table.concat(data["cardNumbers"], ",") or "null");
["Method"] = "GET";
["Headers"] = {
["Content-Type"] = "application/json";
};
});
if (fetchData["Success"]) then
fetchData = http:JSONDecode(fetchData["Body"]);
return {["response"] = "access", ["data"] = fetchData};
else
local msg = "Unknown Error";
local code = "Unknown Code";
pcall(function()
local decodedBody = http:JSONDecode(fetchData["Body"]);
msg = decodedBody["message"];
code = fetchData["StatusCode"];
end)
warn(string.format("Received an error %s response while trying to check an access point scan from the XCS service: %s", code, msg));
return {["response"] = "access", ["data"] = {["response_code"] = "access_denied", ["scan_data"] = {["message"] = msg, ["code"] = code}}};
end
end
else
return {["response"] = "invalid-reader"}
end
else
assert(false, "invalid data sent to the api!");
end
end
local proxService = game:GetService("ProximityPromptService");
proxService.PromptTriggered:Connect(function(obj, player)
if (obj.Name == "xcsInteraction") then
local findApi = obj.Parent.Parent:FindFirstChild("API");
if (findApi) then
-- if proximityPrompAccessWithoutCard is disabled, always deny the request
findApi:Fire({
["request"] = "onScan",
["player"] = player,
["isProximityPrompt"] = true,
["proximityPromptAccessWithoutCard"] = _m.config["proximityPromptAccessWithoutCard"],
["proximityObject"] = obj
});
end
end
end)
_m.main();
-- print("Error while trying to connect to the XCS service!");
-- print("Trying again in 60 seconds..");
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{0216645A-D8E9-4C6D-96F1-A05978DA1E36}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">controller</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
<Item class="ModuleScript" referent="RBXDC842FC31ADB4987B478E0DC92047012">
<Properties>
<Content name="LinkedSource"><null></null></Content>
<ProtectedString name="Source"><![CDATA[local _m = {};
sss = game:GetService("ServerScriptService");
_m.config = nil;
_m.model = nil;
_m.doorId = nil; _m.doorLocked = nil; _m.doorActive = false;
_m.doorTimer = 5;
_m.eventThread = nil;
_m.isSyncing = false;
_m.syncTimerStarted = false;
_m.cardScan = false;
repeat wait(1) until _G.xcsApi ~= nil;
_m.superiorApi = _G.xcsApi;
function _m.findAny(a, b)
for _,c in pairs(b) do
if (a:FindFirstChild(c)) then return a:FindFirstChild(c); end
end
return false;
end
function _m.cancelEventThread()
if (_m.eventThread) then
pcall(function()
task.cancel(_m.eventThread);
end)
_m.eventThread = nil;
end
end
function _m._legacy_trigger(newValue)
if (_m.model:FindFirstChild("Trigger")) then
_m.model:FindFirstChild("Trigger").Value = newValue;
end
end
function _m.negotiate(player, proximityObject, cardNumbers)
local s0, m0 = pcall(function()
if (_m.doorLocked and not _m.cardScan and _m.doorActive) then
if (_m.isSyncing) then return; end
_m.cardScan = true
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["pending"] or _m.sounds["accessPending"];
_m.sound:Play();
end
spawn(function()
if _m.events["accessPending"] or _m.events["pending"] then
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessPending"] or _m.events["pending"], player);
end
end)
if (proximityObject) then
proximityObject.Enabled = false;
end
local checkUser = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="checkAccess";["uid"]=player.UserId;["cardNumbers"]=cardNumbers})["data"];
local accessGranted = (checkUser["response_code"] == "access_granted" and true or false);
if (accessGranted) then
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["granted"] or _m.sounds["accessGranted"];
_m.sound:Play();
end
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessGranted"] or _m.events["granted"], player, checkUser["scan_data"]);
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=true;["player"]=player;});
_m.api:Fire({["request"]="onAccessGranted";["player"]=player;["timerLength"]=_m.doorTimer;});
local time = 0;
local syncTimed = _m.doorTimer;
repeat
wait(1);
time += 1;
until time > syncTimed
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], checkUser["scan_data"]);
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=false;["player"]=player;});
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
else
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["denied"] or _m.sounds["accessDenied"];
_m.sound:Play();
end
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessDenied"] or _m.events["denied"], player, checkUser["scan_data"]);
end)
_m.api:Fire({["request"]="onAccessDenied";["player"]=player;["timerLength"]=_m.config["accessDeniedTimer"];});
wait(_m.config["accessDeniedTimer"]);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], checkUser["scan_data"]);
end)
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
end
_m.cardScan = false
end
end)
if (not s0) then
print(string.format("There was an error while negotiating an access point: %s", m0))
end
end
function _m.touchScan(x)
if (_m.findAny(x.Parent, {"CardNumber", "_xcsCard", "XCS"}) and x.Name == "Handle" and x.Parent.ClassName == "Tool") then
local player = game.Players:GetPlayerFromCharacter(x.Parent.Parent);
local uid = tostring(player.UserId);
local cardNumber = nil;
local cardNumberObject = _m.findAny(x.Parent, {"CardNumber", "_xcsCard", "XCS"});
if (cardNumberObject and (cardNumberObject:IsA("StringValue") or cardNumberObject:IsA("NumberValue") or cardNumberObject:IsA("IntValue"))) then
cardNumber = cardNumberObject.Value;
end
_m.negotiate(player, nil, {cardNumber});
end
end
function readerSettings()
local s0, m0 = pcall(function()
spawn(function()
local proximityObject = _m.model:FindFirstChild("xcsInteraction", true);
local newSettings = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="settings"});
if (newSettings["response"] == "invalid-reader") then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessDisabled"] or _m.events["disabled"], {});
end)
assert(false, string.format("Restrafes XCS (reader): [%s]: Invalid reader ID!\n%s", _m.doorId, _m.model:GetFullName()));
end
newSettings = newSettings["data"];
_m.doorActive = (newSettings["active"]);
_m.doorLocked = (newSettings["armed"]);
_m.api:Fire({["request"]="setDoorTrigger";["value"]=(newSettings["armed"]);["player"]=player;});
_m.indicatorChange("LEDR",(newSettings["active"] == false and 1 or 0));
_m.indicatorChange("LEDG",(newSettings["armed"] == true and 0 or 1));
_m.indicatorChange("LEDS",(newSettings["active"] == true and newSettings["armed"] == true and 1 or 0));
if (_m.doorActive and _m.doorLocked) then
if (not _m.cardScan) then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], newSettings["scanData"]["ready"] or {});
end)
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
end
elseif (_m.doorActive and not _m.doorLocked) then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["enabled"] or _m.events["disarmed"], newSettings["scanData"]["disarmed"] or {});
end)
if (proximityObject) then
proximityObject.Enabled = false;
end
elseif (not _m.doorActive) then
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessDisabled"] or _m.events["disabled"], {});
end)
if (proximityObject) then
proximityObject.Enabled = false;
end
end
_m.doorTimer = tonumber(newSettings["unlockTime"] or 5);
wait(15);
readerSettings();
end)
end)
if (not s0) then
print(string.format("There was an error while grabbing up-to-date reader settings: %s", m0))
end
end
function _m.indicatorChange(N,S)
for _,x in pairs(_m.model:GetChildren()) do
if (x.Name == N) then
x.BrickColor = BrickColor.new(_m.config["_legacy_indicators"][N][S])
if (S == 1) then
x.Material = "Neon";
else
x.Material = "SmoothPlastic";
end
end
end
end
function _m.remoteUnlock(unlockSeconds)
if (not _m.doorActive or _m.cardScan) then return; end
_m.cardScan = true;
if (_m.sound and _m.sounds) then
_m.sound.SoundId = _m.sounds["granted"] or _m.sounds["accessGranted"];
_m.sound:Play();
end
spawn(function()
local proximityObject = _m.model:FindFirstChild("xcsInteraction", true);
if (proximityObject) then
proximityObject.Enabled = false;
end
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessGranted"] or _m.events["granted"], nil, {});
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=true;});
local time = 0;
local syncTimed = tonumber(unlockSeconds) or _m.doorTimer;
_m.api:Fire({["request"]="onAccessGranted";["timerLength"]=syncTimed;});
repeat
wait(1);
time += 1;
until time > syncTimed
spawn(function()
_m.cancelEventThread()
_m.eventThread = task.spawn(_m.events["accessReady"] or _m.events["ready"], {});
end)
_m.api:Fire({["request"]="setDoorTrigger";["value"]=false;});
if (proximityObject and _m.doorActive) then
proximityObject.Enabled = true;
end
_m.cardScan = false;
end)
end
function _m.init(data)
_m.config = data["config"];
_m.model = _m.config["model"];
_m.doorId = _m.config["accessPointId"] or _m.config["doorId"];
_m.sounds = _m.config["sounds"];
_m.events = _m.config["events"];
_m.sound = nil;
_m.api = _m.model.API;
if (_m.model:FindFirstChild("_main")) then
_m.sound = _m.model:FindFirstChild("_main"):FindFirstChild("sound");
if (_m.sound and _m.sounds) then
_m.sounds["granted"] = _m.sounds["granted"] or _m.sounds["accessGranted"] or "";
_m.sounds["denied"] = _m.sounds["denied"] or _m.sounds["accessDenied"] or "";
_m.sounds["pending"] = _m.sounds["pending"] or _m.sounds["accessPending"] or "";
end
end
if (_m.config["_legacy_indicators"] == nil) then
_m.config["_legacy_indicators"] = {
["LEDR"]={[0]="Really black",[1]="Really red"};
["LEDG"]={[0]="Really black",[1]="Lime green"};
["LEDS"]={[0]="Really black",[1]="Institutional white"};
};
end
if (_m.events["accessGranted"] == nil and _m.events["granted"] == nil) then
_m.events["accessGranted"] = function(player)
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessDenied"] == nil and _m.events["denied"] == nil) then
_m.events["accessDenied"] = function(player)
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["enabled"] == nil and _m.events["disarmed"] == nil) then
_m.events["enabled"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",1);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessDisabled"] == nil and _m.events["disabled"] == nil) then
_m.events["disabled"] = function()
_m.indicatorChange("LEDR",1);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
end
end
if (_m.events["accessReady"] == nil and _m.events["ready"] == nil) then
_m.events["accessReady"] = function()
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",1);
end
end
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
local startUp = true;
repeat
_m.indicatorChange("LEDR",1)
local waitPing = _m.superiorApi:Invoke({["reader"]=_m.doorId;["request"]="ping";})
if (waitPing["response"]) then
startUp = false;
break;
end
if (waitPing["response"] == "invalid-reader") then
_m.indicatorChange("LEDR",1);
assert(false, string.format("Restrafes XCS (reader): [%s]: Invalid reader ID!\n%s", _m.doorId, _m.model:GetFullName()));
end
until not startUp
_m.indicatorChange("LEDR",0);
wait(0.2);
_m.indicatorChange("LEDR",1);
wait(0.2);
_m.indicatorChange("LEDG",1);
wait(0.2);
_m.indicatorChange("LEDS",1);
wait(2.5);
_m.indicatorChange("LEDR",0);
_m.indicatorChange("LEDG",0);
_m.indicatorChange("LEDS",0);
if (_m.model:FindFirstChild("_touch")) then
_m.model:FindFirstChild("_touch").Touched:connect(_m.touchScan);
end
if (_m.model:FindFirstChild("_main")) then
_m.model:FindFirstChild("_main").Touched:connect(_m.touchScan);
end
-- new interaction method
_m.api.Event:Connect(function(data)
if (data["request"] == "onScan") then
local cardNumbers = {};
for _,v in pairs(data["player"]:FindFirstChild("Backpack"):GetChildren()) do
if (v:FindFirstChild("CardNumber") and v.ClassName == "Tool") then
table.insert(cardNumbers, v:FindFirstChild("CardNumber").Value);
end
end
for _,v in pairs(data["player"].Character:GetChildren()) do
if (v:FindFirstChild("CardNumber") and v.ClassName == "Tool") then
table.insert(cardNumbers, v:FindFirstChild("CardNumber").Value);
end
end
if (data["isProximityPrompt"]) then
if (data["proximityPromptAccessWithoutCard"]) then
_m.negotiate(data["player"], data["proximityObject"], cardNumbers);
end
else
_m.negotiate(data["player"], nil, cardNumbers);
end
elseif (data["request"] == "setDoorTrigger") then
_m._legacy_trigger(data["value"]);
end
end)
repeat wait() until _G.xcsRemoteUnlock ~= nil;
_G.xcsRemoteUnlock.Event:Connect(function(accessPointId, payload)
if (accessPointId ~= _m.doorId) then return; end
local unlockSeconds = nil;
if (type(payload) == "table") then
unlockSeconds = payload["unlockTime"];
end
_m.remoteUnlock(unlockSeconds);
end)
readerSettings()
end
return _m
]]></ProtectedString>
<string name="ScriptGuid">{B5B5954E-557A-4C96-9012-20855D9E30F8}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">17996771963439872</SecurityCapabilities>
<bool name="DefinesCapabilities">true</bool>
<string name="Name">unit</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
</Item>
</Item>
</Item>
<Item class="BindableEvent" referent="RBX10E8617A8D5647C4A706F5D17DA412B2">
<Properties>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">API</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
</Item>
</Item>
<Item class="Script" referent="RBXC1CBDEF708934E64AF465D3C786DDF7B">
<Properties>
<ProtectedString name="Source"><![CDATA[--[[
MIT License
Copyright (c) 2023 RESTRAFES & CO. LLC.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--]]]]></ProtectedString>
<bool name="Disabled">false</bool>
<Content name="LinkedSource"><null></null></Content>
<token name="RunContext">0</token>
<string name="ScriptGuid">{78B78716-EAE9-4F7E-AC3F-B6940EE03A8A}</string>
<BinaryString name="AttributesSerialize"></BinaryString>
<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
<bool name="DefinesCapabilities">false</bool>
<string name="Name">LICENSE</string>
<int64 name="SourceAssetId">-1</int64>
<SharedString name="Tags">yuZpQdnvvUBOTYh1jqZ2cA==</SharedString>
</Properties>
</Item>
</Item>
<SharedStrings>
<SharedString md5="aTIjYIeEFqyQ2bhuh8rtLg==">Q1NHUEhTCAAAAAIAKLUv/WDEDW0mANQiEgAAACsBAAAOAgAAI0IACgQAAIQEDgAA7FG4veF6
lL6srG28PT48bcstFNduQeHrAsVdLZS7tgsFb1sUbrd1yl3XLVuU2y1RLRS3QGkBbtJSbbXr
um7bcAHgrqVVJ1UC7Vmqb92ivK5yu0W5AC0FgGpVVbW3Ps+q3tqtXaBaABRVVa0WujerqlvX
bu3bckHBC4r3LoJyu3YtrdKWgOJunbZVZrm/AwAAsEg4vfzrgbztUTi9EM6MRfeTad0/Xezi
ehQ+rJg5YEO2CEa39Z9lCeW2N6ZovD0+krd3QGO8QhG4+ktfhUL3k+GamTlSuF48Dj1uEq49
8nmUzxCwPdttBvmtPc72kyy6Xgb/sT25Rxs2sT3hzTKrrz2k62Cesj2EnRLdsz1pXAnitD1a
DbihtT00spLOGbY9i1C545G4POqRHR+4PahpkgGJtz1b5YSWtj3mVeNQtT1ruavDsz1EC5TO
HLi9qWm9PX8Xtr2MvVy9Nb1bw9qzvWpcvYW94r2lt/atvbq9571svUW9ur3cvfN2tl4+y/aT
LOqRvZtpkrKGt71S5c5Wk5ROtb1VulzBs70wDJQY+LG9uEceE7C97WxEpK+9kOx8MbG9zM5/
nb1iXLrftL1ADr0pspK9bFG9qeORPT09PT09PT09PVC8fBQWwLYYxGm8bH9iuGu87KAsdLaY
0LctTGFwc83QRb0AAIAyAOBttwfiu1JET7cUvpRMNz6hawo9sh9qVr2B66jR1pBEqVHZ48Gp
gAxZ3TaREKioUkIVJSlIljG36P19NSaVnLYUhh8RfWdRvA3Z9AL9spQ/ifrjF93u+BDlio1e
knVdtX9mEpY7vRawre1I/ky0iw8X/HlBojb3wNqite/f94/qejz8DKXGc3vCfBMyuHvh/tMe
/fps740Yfue3HK/EWmoETlIKC09s3ItHuhLQKmipFe3pBMcLAEh0+7i/d2v+AqA9gNdOnEcd
7h/H5WZULctuiB48uzb71/sH506nrkn7lfe/5EzqD4ldv4j341NvrmdXos/5Xaf9+zxxHhMx
7oIbX17XgI54Ri+MiJpd2CySHFbFx42MvRsuAMnOnOzYpzKWdHQH7ND16sh6CvEMPHCo9OAW
5zaHC6JJ//gYtffm7C1fpY5iPSV3wp0CFs2E9Ivje170mD/X+14mdq0kHayHZnjqG5uL+lnS
FLWnKSYNKFeCfjUHdL67bWzoAhCT8ttmEva7Gx9kx0jyvhLmHgJJl/hx1MelIU+8bhEfYpTj
bdGvyWu1aBtTHpX21N81HJedvnscC1Pf5xHH9EJk+ELEzkcOgJ3Ig2NfQl2A+K38mUfTYZ9j
dcd7+Lw5BO5ajR9D9B2C6QnzcmY/p2VfJLIasitcmMwvcBFxdj5bvuMAwMvTFmS8TyiGsL2I
WzzwjQTyvzk4pZ3rXQ3uJZ6mKNkinrCLNIHvJe6U6NwCvYLc/qnfzVxDXWsOmd45Kstz1D+4
AeGqMQun/40XOaJg43Oka76Ix/kP8GIj6p2+U9O234z32UtSRfnhWO3nzmMSsNRFu8ieyuyX
X65/JKc8afeH6TJhnbIGjTgWidthBEOH80ZsdrzeInUep+fzIHAR09lq2190t4LMh6fqgXpl
JeyXlfLQLZEK</SharedString>
<SharedString md5="yuZpQdnvvUBOTYh1jqZ2cA=="></SharedString>
<SharedString md5="9youQqgpV70GZVe57RLKHg==">Q1NHUEhTCAAAAAIAKLUv/WCUDl0lAIQdFAAAADwBAAAoAgAAIAA8BAAAiNAOAABJzMy9mpmZ
vs3MzLw9PjwtZxUoBQpet9y167q7dVahu3ULhWVpQXFVFG73ULw9td2ebimtlkK5VoHys+D1
S6296dSiVrWgvBaUu62A8ranLcBZBda1WVVev67rWygAoFsA125uXdd1AMrrul3XpFWv67p1
FACUu67rtgDXdd26hQtvXVDenlWgvD2rlJ9ttW+r1QLsZYa++e+4XmdmPLxHc2mNybxzjeY7
yrybbBdaybxwUj6suDxi+J2822KCHMm9ZmaGPmZmCt/lW8yaq8i+zcxMvetSlzx6q8dMveJ6
lAAApLa8AAAAMmWilDB4mE6LlL6xWsq8zveClD4mg8m8rEnghrs8PT08gZUAQKQ2qw6XpI6U
vs6syj09uJaWh5sZPnpkZrxoYMLldzSsybyDyjPMy7wOpdzVyrw9hvo5yz3xb5Y+zD0tm2PM
PRx6leJmhjzRTbjw0UzPzEyOTbw9oH8+JMA9DjWGsMM907uYWMnGPTgZDFXJPYhSl7ygoZTs
cpmUPv3qy7zuvjzKzNAKatSI5Ow8moVFkZS+chPLvBDXvZ+9+1IMrGNqvX293b31vS69zb2H
7jw9m8KVNp+9PYIeqEJGZaNJUqlsjQFikwIJUarheWIwLR/BRHAURLWWAwd/tNCt9PnwvMbN
LYop0FF3bmHPj9/vocNnLXY3wy76/P97Vt/lfBr+teAS6QVM4KXFNep6+lCiYVjysh+sFBdg
zNn1Mcc8sdr/cLy8QTT4V5tryf3wH2cG2btpC2suVHTe3ZbjMKTVDZNF2q+g4jKxw7PvgUzx
thmBUM33t5eLgSOcqk2d/ijjXKvN9vvpbJ64E4vytzzBDLM6Xxf6K+cc2sN+w/mvs24x+qxf
Dvq/HrkM/DWOpH5o/5WFwJpj7ZI2zbh5+1X0zP34pQ6/pRvRpxreM7lew+X2H/K3d7iW/zP7
h0CckfG14aKZ39k+MlQ/rtScyMemUxhMFyf4LsDmXhzTOdmu2F+NJ0tbpwtzYoQcv/Dh6RxA
TCzDThWH98fYd/1Cu79HCv1/vevUrmKxt+p8bNgmM584vkf1T63DGa9oF02+PMj9hCbhUBRE
8hGbeMHL6VKNcOypOewFTM9YFoH+lNCxONuXy4ydiFrXuXdGL7/wTbm+mDT1i+p1t9vkKQ71
nUnW/DQsfm4EBnLO8XEXGAf99UrhDiIt1f9/jO8u3pfpJ7+OG7VyvBy9rMz+rK++eGZl9ziO
yC71/pLxnn3qZLHEr/3fr3ECzjP7/Xmm/squBT3iN8JWdV6CZ6H/vXo6XOgFvIzh9VbkLO3c
mHGiBaaxBXxNh75o8oiRXvdnyO9sgX5ycfDCwTI5/z8Pdl/93kq56J7oEpv/CStP8kdmDmz3
KGMsjj5i/+FA48J5u759Ip740/P4HNS31UFZvAApdrQTDJcuvEgfGZ1H90Lu44zwyXvEJipM
1ueNVz+vYUX+5G/1lB1fLu+nLXTeHTEJMwcWPUKfJbyxILXP2Qqn576J+15DMY0EDAn6AiYs
LuKtU0gzSvcSdFWagwuh7d7qGDI/hgP8xIsHV7Q=</SharedString>
</SharedStrings>
</roblox>