Key System [V0.1]
I’ve been working on a key system for the past 2 and a half weeks and I wanted to show off my hard work. I used this as a way to experiment with data stores and how to better optimize data stores. I plan to add many more features with time which can be found listed below. Any criticism or feedback on how I can improve this project is greatly appreciated and hopefully open this project up to the public after improving it.
Concept
Initially, I took inspiration from to.yhouse, a website for artists, key system. Toyhouse doesn’t allow users to interact or create content on their website unless they have an account. To create an account on Toyhouse, you must have an auto-generated key. These keys can be provided and generated by other users of the site and owners. This inspired me to take my own twist on it.
My key system restricts players who do not have access to the game from viewing the contents within the game. A key, a randomly generated 12-character code, can be used to gain access to the game’s contents. Keys can also be generated by players by playing the game (not yet implemented), once this key is given to another player and used, the owner of the key is given rewards upon rejoining (30 Coins).
Future Features
- Players can generate keys through actions and playing the game
- Inventory / non-stat rewards
- Ability to list all existing keys
- And more…
How It All Works
Modules scripts were used to better organize most of the code, although still messy.
KeyServerManager handles the generation and management of keys. It saves and loads any current keys that are saved within the game and removes any that have been used. It also handles rewarding players who have shared their respective generated keys with others.
KeyServerManager
local RS = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")
local Main = require(script.Main)
local Rewards = require(script.Main.Rewards)
local InsertKeyEvent = RS:FindFirstChild("InsertKey")
local GenerateKeyEvent = RS:FindFirstChild("GenerateKey")
local RemoveAccess = RS:FindFirstChild("RemovePlayer")
InsertKeyEvent.OnServerEvent:Connect(Main.UseKey)
GenerateKeyEvent.OnServerEvent:Connect(Main.GenerateNewKey)
RS.test.OnServerEvent:Connect(Rewards.SetReward)
RemoveAccess.OnServerEvent:Connect(Main.RemoveAccess)
players.PlayerAdded:Connect(Main.onPlayerAdded)
Main
local KeyServer = {}
local RewardsModule = require(script.Rewards)
local DSS = game:GetService("DataStoreService")
local RS = game:GetService("ReplicatedStorage")
local GenerateKey = RS.GenerateKey
local KeyStorage = DSS:GetDataStore("KeyStorage")
local PlayerData = DSS:GetDataStore("KeyStorage", "Players")
local function GenerateRandomKey(plr)
local characters = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9","0"}
local KeyLength = 12
local Max = #characters
local NewKey = ""
for i = 1, KeyLength do
local RandomCharacter = characters[math.random(1,Max)]
NewKey = NewKey..tostring(RandomCharacter)
end
return NewKey
end
KeyServer.UseKey = function(player, TargetKey)
local success, owner = pcall(function()
return KeyStorage:GetAsync(TargetKey)
end)
if success then
if owner ~= nil and owner ~= "" then
print(owner)
RewardsModule.SetReward(owner)
end
KeyStorage:RemoveAsync(TargetKey)
PlayerData:SetAsync(player.UserId, TargetKey)
print("User: "..player.Name, "Key Used: "..TargetKey)
player.PlayerGui.KeyGUI:Destroy()
return true
else
return false
end
end
KeyServer.GenerateNewKey = function(sender,plr)
local key = GenerateRandomKey() --creates a random key
local success, err = pcall(function()
if plr then
KeyStorage:SetAsync(key, plr.UserId) --Stores the key as available and the player that generated the key
else
KeyStorage:SetAsync(key, "") --Stores the key as available without a player origin
end
end)
if success then
GenerateKey:FireClient(sender, tostring(key))
return key
else
warn("Failed to generate key: "..err)
return nil
end
end
KeyServer.onPlayerAdded = function(player)
local success, usedkey = pcall(function()
return PlayerData:GetAsync(player.UserId)
end)
RewardsModule.OnPlayerAdded(player)
if success and usedkey then
player.CharacterAdded:Wait()
player.PlayerGui.KeyGUI:Destroy()
return
end
end
KeyServer.RemoveAccess = function(player)
local success, usedkey = pcall(function()
return PlayerData:GetAsync(player.UserId)
end)
if success and usedkey then
PlayerData:RemoveAsync(player.UserId)
player:Kick("You may rejoin, but you'll need to provide a new key to regain access.")
end
end
return KeyServer
Rewards
local RewardMod = {}
local DSS = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local KeyStorage = DSS:GetDataStore("KeyStorage")
local PlayerRewards = DSS:GetDataStore("KeyStorage", "RewardData")
local LootTable = require(script.Parent.LootTable)
local Items = {"Rock","Stick","Gun"}
local PlayerStats = {"XP","Coins"}
local function GetRandomReward()
local Total = 0
for _, item in pairs(LootTable) do
Total = Total + item.Weight
end
local RandomVal = math.random(1, Total)
local Current = 0
for _, item in pairs(LootTable) do
Current = item.Weight
if Current >= RandomVal then
if item.Amount then
return item.Name, item.Amount
else
return item.Name, 1
end
end
end
end
local function FindPlayer(Target)
if type(Target) == "number" then Target = game:GetService("Players"):GetPlayerByUserId(Target)end
for _, player in pairs(Players:GetChildren()) do --Checks all players to see if the owner of the key is in the current server
if player == Target then
return player
end
end
return nil
end
local function AddStatItem(player, item, amount)
local TargetStat = player.PlayerData:FindFirstChild(item)
TargetStat.Value += amount
end
local function AddInventoryItem(player, item, amount)
local Inventory = player:FindFirstChild("Inventory")
if Inventory:FindFirstChild(item) then
local item = Inventory:FindFirstChild(item)
item.Value += tonumber(amount)
else
local NewItem = Instance.new("IntValue")
NewItem.Name = item
NewItem.Value = amount
end
end
RewardMod.SetReward = function(playerId, key)
--redefines the player variable in case the player's Id is sent rather than the player instance.
local FoundPlayer = FindPlayer(playerId) --if the player is in the server
local Reward, Amount = GetRandomReward() --Obtains a random reward
local IsRewardStat = table.find(PlayerStats, Reward) --checks if its a stat reward
local IsRewardItem = table.find(Items, Reward) --checks if its an item reward
if FoundPlayer ~= nil then
if IsRewardStat then --Checks if the reward is a stat
AddStatItem(FoundPlayer, Reward, Amount)
print(Reward, Amount)
return
elseif IsRewardItem then --or an item
AddInventoryItem(FoundPlayer, Reward, Amount)
print(Reward, Amount)
return
end
else
local success, err = pcall(function()
return PlayerRewards:SetAsync(playerId, {Name = Reward, Amount = Amount})
end)
if success then
print("Successfully saved!")
else
warn(err)
end
end
end
RewardMod.OnPlayerAdded = function(player)
local reward
print(player.UserId)
local success, err = pcall(function()
reward = PlayerRewards:GetAsync(player)
return reward
end)
local IsRewardStat = table.find(PlayerStats, reward) --checks if its a stat reward
local IsRewardItem = table.find(Items, reward) --checks if its an item reward
local player = game.Players:GetPlayerByUserId(player.UserId)
if reward then
if success then
local RewardName = reward.Name
local Amount = reward.Amount
if IsRewardStat then
AddStatItem(player, RewardName, Amount)
elseif IsRewardItem then
AddStatItem(player, RewardName, Amount)
end
print(RewardName, Amount)
PlayerRewards:RemoveAsync(player.UserId)
else
warn(err)
end
end
end
return RewardMod
LootTable
local LootTable = {
--{Name = Item Name, Weight = #, Amount (optional)};
{Name = "Coins", Weight = 100, Amount = 30};
{Name = "Coins", Weight = 1, Amount = 50};
}
PlayerDataServer handles the creation and data of the player’s inventory and currency. Nothing too special.
PlayerDataServer
local MainMod = require(script.Main)
local RS = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local AddEvent = RS.AddCoins
local SubtractEvent = RS.SubtractCoins
Players.PlayerAdded:Connect(MainMod.OnPlayerAdded)
Players.PlayerRemoving:Connect(MainMod.PlayerRemoving)
AddEvent.OnServerEvent:Connect(MainMod.AddCoins)
SubtractEvent.OnServerEvent:Connect(MainMod.SubtractCoins)
Main
local PlayerMod = {}
local DSS = game:GetService("DataStoreService")
local PlayerDatastore = DSS:GetDataStore("PlayerData")
local StatsDatastore = DSS:GetDataStore("PlayerData", "Coins")
local InvDatastore = DSS:GetDataStore("PlayerData", "Inventory")
PlayerMod.OnPlayerAdded = function(plr)
local PlayerFolder = Instance.new("Folder")
PlayerFolder.Name = "PlayerData"
PlayerFolder.Parent = plr
local InventoryFolder = Instance.new("Folder")
InventoryFolder.Name = "Inventory"
InventoryFolder.Parent = PlayerFolder
local Coins = Instance.new("IntValue")
Coins.Name = "Coins"
Coins.Value = 0
Coins.Parent = PlayerFolder
local Data
local success, err = pcall(function()
Data = StatsDatastore:GetAsync(plr.UserId.."-coins")
end)
if success then
if Data then
Coins.Value = Data
else
print("No data found for player", plr.Name)
end
else
warn(err)
end
end
PlayerMod.AddCoins = function(plr, stat, v)--stat is for the future
if plr and v then
local Coins = plr.PlayerData.Coins
if Coins then
Coins.Value += tonumber(v)
end
end
end
PlayerMod.SubtractCoins = function(plr, stat, v)
if plr and v then
local Coins = plr.PlayerData.Coins
Coins.Value -= tonumber(v)
end
end
PlayerMod.PlayerRemoving = function(plr)
if plr then
local Coins = plr.PlayerData.Coins
local success, err = pcall(function()
StatsDatastore:SetAsync(plr.UserId.."-coins", Coins.Value)
end)
if success then
print("Sucessfully saved player's data!!", plr.Name)
else
warn(err)
end
end
end
return PlayerMod
Here’s the code to the UI in case.
GUI
EnterKey
local RS = game:GetService("ReplicatedStorage")
local UIS = game:GetService("UserInputService")
local InsertKeyEvent = RS:FindFirstChild("InsertKey")
local GenerateKeyEvent = RS:FindFirstChild("GenerateKey")
local Frame = script.Parent
local Textbox = Frame.TextBox
local EnterButton = Frame.EnterButton
local WarningLabel = Frame.WarningLabel
local SuccessLabel = Frame.SuccessLabel
local EnterKey = Enum.KeyCode.KeypadEnter
UIS.InputBegan:Connect(function(input, gp) --when the player stops interacting with it
if UIS:GetFocusedTextBox() == Textbox then
if not gp then
if input.KeyCode == EnterKey then
local TargetKey = tostring(Textbox.Text)
InsertKeyEvent:FireServer(TargetKey)
end
end
end
end)
EnterButton.Activated:Connect(function()
local TargetKey = tostring(Textbox.Text)
InsertKeyEvent:FireServer(TargetKey)
end)
GenerateKeyEvent.OnClientEvent:Connect(function(key)
if key == nil then
task.spawn(function()
WarningLabel.Visible = true
task.wait(5)
WarningLabel.Visible = false
end)
end
end)
Game
To create a key, simply click “Create Key”
To create a key that provides rewards, simply click “Create Reward” and provide the key to another player
To Remove yourself from the datastore and return back to the menu simply click “Remove Access” and rejoin