2025-06-29 23:59:43 +08:00
|
|
|
--[[
|
|
|
|
Evercyan @ March 2023
|
|
|
|
ServerMain
|
|
|
|
|
|
|
|
ServerMain requires many important modules for the game to work, as well as housing some miscellaneous code.
|
|
|
|
The modules it requires is under ServerStorage.Modules and the script itself, which makes core features of the game work,
|
|
|
|
such as armor, mobs, and the leveling system.
|
|
|
|
]]
|
|
|
|
|
|
|
|
--> Services
|
|
|
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
|
|
local ServerStorage = game:GetService("ServerStorage")
|
|
|
|
local Players = game:GetService("Players")
|
|
|
|
|
|
|
|
--> References
|
|
|
|
local PlayerData = ReplicatedStorage:WaitForChild("PlayerData")
|
|
|
|
|
|
|
|
--> Dependencies
|
|
|
|
local HumanoidAttributes = require(script.HumanoidAttributes)
|
|
|
|
local PlayerLeveling = require(script.PlayerLeveling)
|
|
|
|
|
|
|
|
local GameConfig = require(ReplicatedStorage.Data.GameConfig)
|
|
|
|
local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
|
|
|
|
local ToolLib = require(ServerStorage.Modules.ToolLib)
|
|
|
|
local ArmorLib = require(ServerStorage.Modules.ArmorLib)
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Create initial folders
|
|
|
|
local function CreateFolder(Name: string, Parent: Instance)
|
|
|
|
local Folder = Instance.new("Folder")
|
|
|
|
Folder.Name = Name
|
|
|
|
Folder.Parent = Parent
|
|
|
|
return Folder
|
|
|
|
end
|
|
|
|
|
|
|
|
local Temporary = CreateFolder("Temporary", workspace)
|
|
|
|
local ProjectileCache = CreateFolder("ProjectileCache", Temporary)
|
|
|
|
local Characters = CreateFolder("Characters", workspace)
|
|
|
|
|
|
|
|
local function OnPlayerAdded(Player: Player)
|
|
|
|
local pData = PlayerData:WaitForChild(Player.UserId)
|
|
|
|
local Level = pData.Stats.Level
|
|
|
|
local XP = pData.Stats.XP
|
|
|
|
|
|
|
|
local function OnCharacterAdded(Character: Model)
|
|
|
|
local Humanoid = Character:WaitForChild("Humanoid") :: Humanoid
|
|
|
|
local HumanoidAttributes = HumanoidAttributes.new(Humanoid)
|
|
|
|
|
|
|
|
local s = tick()
|
|
|
|
while Character.Parent ~= Characters and (tick()+5 > s) do
|
|
|
|
task.wait()
|
|
|
|
pcall(function()
|
|
|
|
Character.Parent = Characters
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if Player.Character then
|
|
|
|
task.spawn(OnCharacterAdded, Player.Character)
|
|
|
|
end
|
|
|
|
Player.CharacterAdded:Connect(OnCharacterAdded)
|
|
|
|
|
|
|
|
-- Starter items
|
|
|
|
-- 这里不会每次进入都给玩家装备吗?
|
|
|
|
for _, StarterItem in GameConfig.StarterItems do
|
|
|
|
local Module = require(ServerStorage.Modules[StarterItem[1] .."Lib"])
|
|
|
|
Module:Give(Player, ContentLibrary[StarterItem[1]][StarterItem[2]])
|
|
|
|
end
|
|
|
|
|
|
|
|
-- If the hotbar is completely empty, fill it with starter items
|
|
|
|
pData:WaitForChild("Hotbar")
|
|
|
|
local isEmpty = true
|
|
|
|
for _, ValueObject in pData.Hotbar:GetChildren() do
|
|
|
|
if ValueObject.Value ~= "" then
|
|
|
|
isEmpty = false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if isEmpty then
|
|
|
|
for n, StarterItem in GameConfig.StarterItems do
|
|
|
|
if n <= 9 then
|
|
|
|
pData.Hotbar[tostring(n)].Value = StarterItem[2]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Player leveling
|
|
|
|
-- 登录检测经验升级
|
|
|
|
PlayerLeveling:TryLevelUp(Player, Level, XP)
|
|
|
|
-- 检测经验变化升级
|
|
|
|
XP.Changed:Connect(function()
|
|
|
|
PlayerLeveling:TryLevelUp(Player, Level, XP)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
Players.PlayerAdded:Connect(OnPlayerAdded)
|
|
|
|
for _, Player in Players:GetPlayers() do
|
|
|
|
task.spawn(OnPlayerAdded, Player)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Initially require all server-sided & shared modules
|
2025-06-30 00:06:14 +08:00
|
|
|
-- 加载模块 ReplicatedStorage.Modules, ServerStorage.Modules
|
2025-06-29 23:59:43 +08:00
|
|
|
for _, Location in {ReplicatedStorage.Modules, ServerStorage.Modules} do
|
|
|
|
for _, Library in Location:GetChildren() do
|
|
|
|
if Library:IsA("ModuleScript") then
|
|
|
|
task.spawn(require, Library)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
---- Hotbar Persistence --------------------------------------------------------
|
|
|
|
|
|
|
|
ReplicatedStorage.Remotes.HotbarItemChanged.OnServerEvent:Connect(function(Player, SlotNumber: number, ItemName: string)
|
|
|
|
if not SlotNumber or typeof(SlotNumber) ~= "number" then return end
|
|
|
|
if not ItemName or typeof(ItemName) ~= "string" or #ItemName > 200 then return end
|
|
|
|
|
|
|
|
local pData = PlayerData:FindFirstChild(Player.UserId)
|
|
|
|
local Hotbar = pData and pData:FindFirstChild("Hotbar")
|
|
|
|
local ValueObject = Hotbar and Hotbar:FindFirstChild(SlotNumber)
|
|
|
|
|
|
|
|
if ValueObject then
|
|
|
|
ValueObject.Value = ItemName
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
---- Shop ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
ReplicatedStorage.Remotes.BuyItem.OnServerInvoke = function(Player, ItemType: string, ItemName: string)
|
|
|
|
if not ItemType or not ItemName then return end
|
|
|
|
if typeof(ItemType) ~= "string" or typeof(ItemName) ~= "string" then return end
|
|
|
|
|
|
|
|
local pData = PlayerData:FindFirstChild(Player.UserId)
|
|
|
|
local Item = ContentLibrary[ItemType] and ContentLibrary[ItemType][ItemName]
|
|
|
|
if not pData or not Item then return end
|
|
|
|
|
|
|
|
if not Item.Config.Cost then
|
|
|
|
return false, "This item isn't for sale"
|
|
|
|
end
|
|
|
|
|
|
|
|
if pData.Items[ItemType]:FindFirstChild(ItemName) then
|
|
|
|
return false, "You already own this item"
|
|
|
|
end
|
|
|
|
|
|
|
|
if pData.Stats.Level.Value < Item.Config.Level then
|
|
|
|
return false, "Your level is too low to purchase this item"
|
|
|
|
end
|
|
|
|
|
|
|
|
local Currency = pData.Stats[Item.Config.Cost[1]]
|
|
|
|
|
|
|
|
if Currency.Value < Item.Config.Cost[2] then
|
|
|
|
return false, "Your gold is too low to purchase this item"
|
|
|
|
end
|
|
|
|
|
|
|
|
Currency.Value -= Item.Config.Cost[2]
|
|
|
|
|
|
|
|
local Module = require(ServerStorage.Modules[ItemType .."Lib"])
|
|
|
|
Module:Give(Player, Item)
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
ReplicatedStorage.Remotes.SellItem.OnServerInvoke = function(Player, ItemType: string, ItemName: string)
|
|
|
|
if not ItemType or not ItemName then return end
|
|
|
|
if typeof(ItemType) ~= "string" or typeof(ItemName) ~= "string" then return end
|
|
|
|
|
|
|
|
local pData = PlayerData:FindFirstChild(Player.UserId)
|
|
|
|
local Item = ContentLibrary[ItemType] and ContentLibrary[ItemType][ItemName]
|
|
|
|
if not pData or not Item then return end
|
|
|
|
|
|
|
|
if not Item.Config.Sell and not Item.Config.Cost then
|
|
|
|
return false, "This item can't be sold"
|
|
|
|
end
|
|
|
|
|
|
|
|
if not pData.Items[ItemType]:FindFirstChild(ItemName) then
|
|
|
|
return false, "You don't own this item"
|
|
|
|
end
|
|
|
|
|
|
|
|
local Currency = pData.Stats[Item.Config.Sell and Item.Config.Sell[1] or Item.Config.Cost[1]]
|
|
|
|
local Return = Item.Config.Sell and Item.Config.Sell[2] or math.floor(Item.Config.Cost[2] / 2)
|
|
|
|
|
|
|
|
local Module = require(ServerStorage.Modules[ItemType .."Lib"])
|
|
|
|
Module:Trash(Player, Item)
|
|
|
|
|
|
|
|
Currency.Value += Return
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|