콘텐츠로 이동

설치

게임에는 ServerStorage.Devplorer 폴더 하나만 넣으면 되며, 그 안에는 다음 두 항목이 있습니다.

이름 종류 설명
Server ModuleScript 서버 모듈입니다. 이 아래에 Fiu 모듈이 함께 들어갑니다.
Client ScreenGui 클라이언트 번들입니다. UI 코드와 라이브러리가 들어 있으며, 평소에는 아무에게도 복제되지 않습니다.

저장소의 default.project.json에는 DevplorerDev와 DevplorerDevBoot도 있습니다. 이 둘은 이 저장소의 개발 플레이스 전용이므로 게임에 넣을 필요가 없습니다.

저장소를 게임 프로젝트 옆(또는 안)에 두고 scripts/vendor.sh를 한 번 실행합니다. 이 스크립트는 vendor.lock.json에 고정된 라이브러리(quad, Fiu)를 vendor/ 아래에 내려받습니다. 인터프리터용 컴파일러도 같은 곳에 내려받으며, 인터프리터를 쓰지 않더라도 그대로 두면 됩니다.

터미널 창
scripts/vendor.sh

vendor/는 git에 올라가지 않으며, 고정된 해시로 언제든 다시 만들 수 있습니다.

2. 프로젝트 파일에 트리 추가하기

섹션 제목: “2. 프로젝트 파일에 트리 추가하기”

게임의 Rojo 프로젝트 파일(default.project.json)의 ServerStorage 아래에 다음을 추가합니다. devplorer/ 부분은 저장소를 둔 상대 경로로 바꿔 주세요.

"ServerStorage": {
"$className": "ServerStorage",
"Devplorer": {
"$className": "Folder",
"Server": {
"$path": "devplorer/src/Server",
"Fiu": {
"$path": "devplorer/vendor/fiu/Source.lua"
}
},
"Client": {
"$className": "ScreenGui",
"Boot": {
"$path": "devplorer/src/Client/Boot.client.luau"
},
"App": {
"$path": "devplorer/src/Client/App"
},
"Shared": {
"$path": "devplorer/src/Client/Shared"
},
"quad-roblox": {
"$className": "Folder",
"src": {
"$path": "devplorer/vendor/quad/quad-roblox/src"
},
"luau_packages": {
"$path": "devplorer/vendor/quad/quad-roblox/luau_packages"
}
},
"quad-roblox-devui": {
"$className": "Folder",
"src": {
"$path": "devplorer/vendor/quad/quad-roblox-devui/src"
}
},
"$properties": {
"ResetOnSpawn": false,
"ZIndexBehavior": "Sibling",
"DisplayOrder": 1000,
"Enabled": true
}
}
}
}

서버 Script(예: ServerScriptService 아래)에서 Server.Start를 한 번 호출합니다.

local Devplorer = require(game.ServerStorage.Devplorer.Server)
Devplorer.Start({
Provider = function(player: Player): string?
-- 판정 함수는 아래에서 설명합니다
return nil
end,
})

Provider를 넘기지 않으면 아무에게도 권한이 없으며, 이 경우 콘솔 명령(Grant)으로만 권한을 줄 수 있습니다.

  • Start는 서버 Script에서 직접 호출해 주세요. pcall이나 task.spawn 안에서 호출하면 devplorer가 호출한 스크립트를 찾지 못해 경고를 출력하고, 그 스크립트를 편집으로부터 보호할 수 없습니다.
  • 이미 시작한 뒤에 다시 호출하면 아무 일도 일어나지 않습니다.
  • 모든 설정은 Start 설정에서 설명합니다.

Provider는 (player) -> 레벨? 형태의 함수입니다. 레벨 이름("Tester", "MainTester", "Developer")을 돌려주거나, 권한이 없으면 nil을 돌려줍니다. 요청마다 호출되므로 가볍게 작성해야 합니다.

local Developers: { [number]: boolean } = {
[1234567] = true,
}
local MainTesters: { [number]: boolean } = {
[2345678] = true,
}
local Testers: { [number]: boolean } = {
[3456789] = true,
}
Devplorer.Start({
Provider = function(player: Player): string?
if Developers[player.UserId] then
return "Developer"
elseif MainTesters[player.UserId] then
return "MainTester"
elseif Testers[player.UserId] then
return "Tester"
end
return nil
end,
})

Devplorer.MockProvider는 Studio 안에서는 모두를 Developer로 판정하고, Studio 밖에서는 아무에게도 레벨을 주지 않습니다. Studio에서 먼저 시험해 볼 때 쓸 수 있습니다.

Devplorer.Start({ Provider = Devplorer.MockProvider })

Start만으로는 창이 열리지 않습니다. 서버가 Devplorer.Open(대상)을 호출해야 해당 플레이어에게 번들이 복제됩니다. Open은 권한을 다시 확인하므로, 권한이 없는 사람에게 호출해도 창은 열리지 않습니다.

여는 방법은 게임에 맞게 정할 수 있습니다. 대표적인 방법은 다음과 같습니다.

서버 콘솔(F9)에서 개발자가 직접 호출합니다.

local D = require(game.ServerStorage.Devplorer.Server)
D.Open("Username")

방법 2. 플레이어가 들어올 때 자동으로

섹션 제목: “방법 2. 플레이어가 들어올 때 자동으로”
local Players = game:GetService("Players")
local function open(player: Player)
pcall(Devplorer.Open, player)
end
Players.PlayerAdded:Connect(open)
for _, player in Players:GetPlayers() do
task.spawn(open, player)
end

권한이 없는 플레이어에게는 Open이 에러를 내고 감사 기록에 Denied가 남기 때문에 pcall로 감쌌습니다. 권한이 있는 사람이 많은 게임이라면 자동으로 여는 것보다 아래 방법을 권합니다.

방법 3. 게임이 만든 단축키 또는 채팅 명령

섹션 제목: “방법 3. 게임이 만든 단축키 또는 채팅 명령”

키 입력과 채팅 명령은 클라이언트에서만 감지할 수 있는데, devplorer는 모든 클라이언트에 키 감지 코드를 넣지 않습니다. 그래서 게임의 원격 객체 하나로 서버에 창을 열어 달라고 요청하는 방식을 씁니다.

서버 Script:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local request = Instance.new("RemoteEvent")
request.Name = "OpenDevplorer"
request.Parent = ReplicatedStorage
local lastAsk: { [Player]: number } = {}
request.OnServerEvent:Connect(function(player: Player)
-- Open에는 요청 빈도 제한이 없으므로 게임이 간격을 둡니다
local now = os.clock()
if now - (lastAsk[player] or -10) < 2 then
return
end
lastAsk[player] = now
-- 권한은 Open이 다시 확인합니다. 권한이 없으면 에러가 나므로 pcall
pcall(Devplorer.Open, player)
end)
Players.PlayerRemoving:Connect(function(player: Player)
lastAsk[player] = nil
end)

게임의 LocalScript (단축키 예시):

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local request = ReplicatedStorage:WaitForChild("OpenDevplorer") :: RemoteEvent
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessed: boolean)
if gameProcessed then
return
end
if input.KeyCode == Enum.KeyCode.F10 then
request:FireServer()
end
end)