Player Classes didn't work until I defined Base Classes
19 replies, posted
So I haven't coded in a while and wanted to get back into it. So I decided to peruse the official Garry's Mod Wiki and set up a custom gamemode along with some simple Player Classes to get started. I followed everything step by step, but my player classes still weren't working. So, I looked at the code for Sandbox, wondering what I was doing wrong. I then saw that in the cl_init, init, and shared.lua
[code]DEFINE_BASECLASS( "gamemode_base" )[/code]
was at the top of every file under includes. Why is it done with this method rather than just...
[code]self.BaseClass.PlayerSpawn( self, ply )[/code]
I looked around everywhere and I can't seem to find any information about this little bit of code. As a beginner Lua Scripter, I want to be able to know exactly what each piece of code is doing. So, what exactly is this doing, and why wont any of my player classes work unless it's defined.
Also, if it helps, my custom gamemode is deriving from base.
Example meta-table loader to make global CONST vars for each metatable available to us.
[url]https://dl.dropboxusercontent.com/u/26074909/tutoring/metatables/__metatable_loader.lua.html[/url]
Archives of each what functions are available to us for each realm CLIENT as cl_ and SERVER as sv_ for each of the meta-tables. Choose from sorted or unsorted.
[url]https://dl.dropboxusercontent.com/u/26074909/tutoring/metatables/__metatables.rar[/url]
[url]https://dl.dropboxusercontent.com/u/26074909/tutoring/metatables/__metatables_sorted_func_names.rar[/url]
To define a base-gamemode, you need to set it in your game-mode-folder/game-mode-folder.txt
so:
[lua]"myfoldername"
{
"base" "base"
"title" "MyGameMode"
"maps" "^cs_"
"menusystem" "1"
"settings"
{
}
}[/lua]
That's really all there is to it. You could use:
[lua]"base" "sandbox"[/lua]
if you wanted to.
I already have a file in my gamemode folder called customgamemode.txt. The code is as follows...
[code]"customgamemode"
{
"base" "base"
"title" "My Gamemode"
"maps" "^ff_"
"menusystem" "1"
"settings"
{
}
}[/code]
The gamemode works fine without DEFINE_BASECLASS( "gamemode_base" ), but Player Classes do not.
[QUOTE=Acecool;44673923]Is your folder name "customgamemode" ?[/QUOTE]
Yes it is, and everything you sent me still doesn't explain why
[code]DEFINE_BASECLASS( "gamemode_base" )[/code]
is required for my Player Classes to work. And if all it is doing is telling my gamemode to derive from base, then why isn't it already deriving from base based off the txt file? Like I said previously, Sandbox uses the same code, which is why I tried it in the first place, and I'm simply asking for an explanation as to what that one line of code is doing. There is no documentation on the Wiki, and it doesn't say under Gamemode Creation or Player Classes that it's required. But it was the only thing that got my Player Classes to work.
I derive from sandbox and don't use that line at all and player classes work fine...? You shouldn't need it for them to work. All it does is allows you to inherit stuff.
It shouldn't be gamemode_base, it should just be base, or sandbox. sandbox derives from base.
Show us a photo of your folder structure; do this by opening explorer view and open all trees - for the main folder view, open the root of your gamemode located at gamemodes/<yourgamemode>/ which contains the txt, then snap a photo. In that same photo you can include the txt file open for us to see, or paste the contents here.
Chances are there's a typo, or you're using caps, or you have magic characters in the name, etc...
gamemodes/sandbox/sandbox.txt
[lua]"sandbox"
{
"base" "base"
"title" "Sandbox"
"maps" "^gm_|^gmod_"
"menusystem" "1"
"settings"
{
1
{
"name" "physgun_limited"
"text" "limit_physgun"
"help" "When enabled the physgun can only pick up props and ragdolls"
"type" "CheckBox"
"default" "0"
}
2
{
"name" "sbox_noclip"
"text" "noclip"
"help" "When enabled players will be able to noclip"
"type" "CheckBox"
"default" "1"
}
3
{
"name" "sbox_godmode"
"text" "god_mode"
"help" "When enabled all players will be invincible"
"type" "CheckBox"
"default" "1"
}
4
{
"name" "sbox_playershurtplayers"
"text" "players_damage_players"
"help" "If enabled player's will be able to hurt each other"
"type" "CheckBox"
"default" "1"
}
5
{
"name" "sbox_maxprops"
"text" "max_props"
"help" "Maximum props a single player can create"
"type" "Numeric"
"default" "200"
}
6
{
"name" "sbox_maxragdolls"
"text" "max_ragdolls"
"help" "Maximum ragdolls a single player can create"
"type" "Numeric"
"default" "10"
}
7
{
"name" "sbox_maxvehicles"
"text" "max_vehicles"
"help" "Maximum vehicles a single player can create"
"type" "Numeric"
"default" "4"
}
8
{
"name" "sbox_maxeffects"
"text" "max_effects"
"help" "Maximum effect props a single player can create"
"type" "Numeric"
"default" "200"
}
9
{
"name" "sbox_maxballoons"
"text" "max_balloons"
"help" "Maximum balloons a single player can create"
"type" "Numeric"
"default" "100"
}
10
{
"name" "sbox_maxnpcs"
"text" "max_npcs"
"help" "Maximum NPCs a single player can create"
"type" "Numeric"
"default" "10"
}
11
{
"name" "sbox_maxsents"
"text" "max_entities"
"help" "Maximum entities a single player can create"
"type" "Numeric"
"default" "100"
}
12
{
"name" "sbox_maxdynamite"
"text" "max_dynamite"
"help" "Maximum dynamites a single player can create"
"type" "Numeric"
"default" "10"
}
13
{
"name" "sbox_maxlamps"
"text" "max_lamps"
"help" "Maximum lamps a single player can create"
"type" "Numeric"
"default" "3"
}
14
{
"name" "sbox_maxlights"
"text" "max_lights"
"help" "Maximum lights a single player can create"
"type" "Numeric"
"default" "5"
}
15
{
"name" "sbox_maxwheels"
"text" "max_wheels"
"help" "Maximum wheels a single player can create"
"type" "Numeric"
"default" "50"
}
16
{
"name" "sbox_maxthrusters"
"text" "max_thrusters"
"help" "Maximum thrusters a single player can create"
"type" "Numeric"
"default" "50"
}
17
{
"name" "sbox_maxhoverballs"
"text" "max_hoverballs"
"help" "Maximum hoverballs a single player can create"
"type" "Numeric"
"default" "50"
}
18
{
"name" "sbox_maxbuttons"
"text" "max_buttons"
"help" "Maximum buttons a single player can create"
"type" "Numeric"
"default" "50"
}
19
{
"name" "sbox_maxemitters"
"text" "max_emitters"
"help" "Maximum emitters a single player can create"
"type" "Numeric"
"default" "20"
}
20
{
"name" "sbox_weapons"
"text" "enable_weapons"
"type" "CheckBox"
"default" "1"
}
21
{
"name" "sbox_bonemanip_npc"
"text" "bone_manipulate_npcs"
"help" "If enabled then manipulating NPC bones will be allowed"
"type" "CheckBox"
"default" "1"
}
22
{
"name" "sbox_bonemanip_player"
"text" "bone_manipulate_players"
"help" "If enabled then manipulating Player bones will be allowed"
"type" "CheckBox"
"default" "0"
}
23
{
"name" "sbox_bonemanip_misc"
"text" "bone_manipulate_others"
"help" "If enabled then manipulating the bones of other entities will be allowed"
"type" "CheckBox"
"default" "0"
}
24
{
"name" "sbox_persist"
"text" "persistent_mode"
"help" "Set to anything but 0 to enable persistence mode"
"type" "Text"
"default" ""
}
}
}
[/lua]
gamemodes/terrortown/terrortown.lua
[lua]"terrortown"
{
"base" "base"
"title" "Trouble in Terrorist Town"
"maps" "^ttt_"
"menusystem" "1"
"settings"
{
// TODO: put some settings here
}
}
[/lua]
Also, when you say hey aren't working, what do you mean exactly? How aren't they working? Might seem like a silly question but it might give us more of an idea on what's going on.
Solved it... Kinda. Found a work around rather. In order for Player Classes to work with the base gamemode I had to do
[code]BaseClass.PlayerSpawn( self, ply )[/code] under GM:PlayerSpawn in order for the Player Class to work properly. This also meant that I had to do [code]DEFINE_BASECLASS( "gamemode_base" )[/code] at the top of the lua file. I found that I can just do [code]self.BaseClass.PlayerSpawn( self, ply )[/code] and it works fine.
My only concern though is why define the base class and then do BaseClass.PlayerSpawn instead of just doing self.BaseClass.Playerspawn. And why on the Gmod Wiki does it say that you only need [code]player_manager.SetPlayerClass( ply, "player_custom" )[/code] under GM:PlayerSpawn when you also need [code]self.BaseClass.PlayerSpawn( self, ply )[/code] as well?
[QUOTE=-FrozenFire-;44674121]Solved it... Kinda. Found a work around rather. In order for Player Classes to work with the base gamemode I had to do
[code]BaseClass.PlayerSpawn( self, ply )[/code] under GM:PlayerSpawn in order for the Player Class to work properly. This also meant that I had to do [code]DEFINE_BASECLASS( "gamemode_base" )[/code] at the top of the lua file. I found that I can just do [code]self.BaseClass.PlayerSpawn( self, ply [/code] and it works fine.
My only concern though is why define the base class and then do BaseClass.PlayerSpawn instead of just doing self.BaseClass.Playerspawn. And why on the Gmod Wiki does it say that you only need [code]player_manager.SetPlayerClass( ply, "player_custom" )[/code] under GM:PlayerSpawn when you also need [code]self.BaseClass.PlayerSpawn( self, ply )[/code] as well?[/QUOTE]If you use GM:PlayerSpawn your overriding that function so you will lose the base gamemode functionality which you'll need for player classes to work. Using self.BaseClass.PlayerClass will make sure it keeps the code from the base gamemode by inheriting it. Just for reference, here's whats in the base gamemode's PlayerSpawn:
[lua]
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawn( )
Desc: Called when a player spawns
-----------------------------------------------------------]]
function GM:PlayerSpawn( pl )
--
-- If the player doesn't have a team in a TeamBased game
-- then spawn him as a spectator
--
if ( GAMEMODE.TeamBased && ( pl:Team() == TEAM_SPECTATOR || pl:Team() == TEAM_UNASSIGNED ) ) then
GAMEMODE:PlayerSpawnAsSpectator( pl )
return
end
-- Stop observer mode
pl:UnSpectate()
pl:SetupHands()
player_manager.OnPlayerSpawn( pl )
player_manager.RunClass( pl, "Spawn" )
-- Call item loadout function
hook.Call( "PlayerLoadout", GAMEMODE, pl )
-- Set player model
hook.Call( "PlayerSetModel", GAMEMODE, pl )
end
[/lua]
I think he's just curious why this is done instead of using self.BaseClass.
Simply for efficiency and optimization purposes?
[QUOTE=Acecool;44673988]It shouldn't be gamemode_base, it should just be base, or sandbox. sandbox derives from base.[/QUOTE]
When you are using DEFINE_BASELCASS you do need to put gamemode_ infront of it.
[URL="https://github.com/garrynewman/garrysmod/blob/master/garrysmod/lua/includes/modules/gamemode.lua#L60"]lua/includes/modules/gamemode.lua#L60[/URL]
[lua] -- Using baseclass for gamemodes kind of sucks, because
-- the base gamemode is called "base" - and they have to all be unique.
-- so here we prefix the gamemode name with "gamemode_" - and when using
-- DEFINE_BASECLASS you're expected to do the same.
--
baseclass.Set( "gamemode_" .. name, t )[/lua]
[editline]29th April 2014[/editline]
[QUOTE=-FrozenFire-;44674121]And why on the Gmod Wiki does it say that you only need [code]player_manager.SetPlayerClass( ply, "player_custom" )[/code] under GM:PlayerSpawn when you also need [code]self.BaseClass.PlayerSpawn( self, ply )[/code] as well?[/QUOTE]
It's right in that you only need player_manager.SetPlayerClass to apply the class.
To make things happen based on the class you still need to actually call the function via the player_manager.RunClass (you only need player_manager.OnPlayerSpawn to deal with players with no class).
Since you override the spawn hook and were not calling player_manager.RunClass(ply,'Spawn') , nothing was really happening. When you added self.BaseClass.PlayerSpawn, you made it call the function from the base gamemode, which does contain player_manager.RunClass(ply,'Spawn') , making it work.
I hope that makes sense and helps.
He's including the game-mode incorrectly; I've asked for information to help resolve it, yet he continues to do crummy work-arounds.
Do you want it to be right, or just slap it together so it "works"?
Just because it works, doesn't make it right. - Josh 'Acecool' Moser first said around 2 and a half decades ago.
Show us what I've asked for so we can help you make it right. If it is done correctly, you don't need to do the define base-class. You don't need to run base-class unless you overwrite it, but usually in a game-mode when you overwrite a function you're trying to change the entire functionality of it. If you're just adding functionality, you hook.Add into it.
[QUOTE=Acecool;44674686]If it is done correctly, you don't need to do the define base-class. You don't need to run base-class unless you overwrite it, but usually in a game-mode when you overwrite a function you're trying to change the entire functionality of it. If you're just adding functionality, you hook.Add into it.[/QUOTE]
It sounds exactly like thats what they did, overrode the function without including the needed parts from the base method. The whole baseclass thing can be removed if self.BaseClass.PlayerSpawn( self, ply ) is changed to player_manager.RunClass(ply,'Spawn')
Also I must be doing it incorrectly as well as thats how I've always done it. You set the base in the text file and that works, but if you want to call any methods from BaseClass that have been overriden you need to use DEFINE_BASECLASS. Is that wrong?
It sounds like his game-mode isn't set up properly. Hey, do us a favor OP. Set up console logging and create a log of you starting your server, join it, retry, disconnect, shut-down.
Post the log here. If you have an error saying something like no base set, or something along those lines then it's not setup correctly, but posting the log may help...
[QUOTE=Acecool;44674825]It sounds like his game-mode isn't set up properly. Hey, do us a favor OP. Set up console logging and create a log of you starting your server, join it, retry, disconnect, shut-down.
Post the log here. If you have an error saying something like no base set, or something along those lines then it's not setup correctly, but posting the log may help...[/QUOTE]Why does he need to do that? I thought he already got it working. I think the reason it wasn't working is because he was overriding GM:PlayerSpawn which is where the vital calls for player classes to work are placed in the base gamemode. When he used BaseClass, he restored the hook's default functionality as it inherited the base gamemode's code.
FrozenFire, you think you can try something out? Firstly, remove or comment out all the BaseClass stuff you added. After that, copy and paste the base gamemode's PlayerSpawn code and adjust it to work with your player classes. Then, see how that works for you. Since it would be using the same code as in the base gamemode, it should work without the need for any inheritance or base classes.
If anyone disagrees with me or anything, please feel free to correct me.
[QUOTE=Acecool;44674686]He's including the game-mode incorrectly; I've asked for information to help resolve it, yet he continues to do crummy work-arounds.[/QUOTE]
I'm not including anything incorrectly, and because you keep insisting on seeing my exact code, here you go. [URL]https://www.dropbox.com/s/dxgfv7o82hqbkxe/customgamemode.rar[/URL]
It's just a simple gamemode I created so I could start fiddling with Player Classes. The gamemode itself works absolutely fine. No errors at all in console. Now I'm just asking why you would do
[code]DEFINE_BASECLASS( "gamemode_base" )[/code] and then have to do [code]BaseClass.PlayerSpawn( self, ply )[/code]
when you can just simply do [code]self.BaseClass.PlayerSpawn( self, ply)[/code]
Unless defining the base class is that much of an improvement in performance, which at first glance it doesn't seem like it.
[QUOTE=ShadowRanger;44674939]FrozenFire, you think you can try something out? Firstly, remove or comment out all the BaseClass stuff you added. After that, copy and paste the base gamemode's PlayerSpawn code and adjust it to work with your player classes. Then, see how that works for you. Since it would be using the same code as in the base gamemode, it should work without the need for any inheritance or base classes.[/QUOTE]
Alright, I'll try that in a bit, or you can download the file and try it too. But yah, I figured out the spawn stuff, I was overwriting the PlayerSpawn and not calling the base PlayerSpawn. But now I'm just trying to find out what way is better.
With the way you're adding the code before calling the base-class stuff; you could simply use a hook.Add. If you needed the class to be set after then you'd either use a timer within a hook.Add ( not a good solution ), or overwrite the function.
Just seems odd that you're doing it that way.
I did a quick test in my game-mode which only uses the TXT set to base without using DEFINE_BASECLASS.
Try using:
[lua]function GM:PlayerSpawn( _p )
PrintTable( self.BaseClass );
end[/lua]
or adding the PrintTable to yours without the define. As long as the naming is all fine, then you should be able to access the baseclass without defining it again.
Additionally: player_manager.RegisterClass( "player_custom", PLAYER, "player_default" ); registers player_default as the base-class, so any function you omit in your new player-class will be copied over / redirected from the player_default automatically ( edit: And you should be able to call the base-class within a function you override as well ).
[QUOTE=Acecool;44676048]Just seems odd that you're doing it that way.[/QUOTE]
It's funny you say that because I'm doing it the exact same way as sandbox minus the DEFINE_BASECLASS.
[code]---- Make BaseClass available
--
DEFINE_BASECLASS( "gamemode_base" )
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawn( )
Desc: Called when a player spawns
-----------------------------------------------------------]]
function GM:PlayerSpawn( pl )
player_manager.SetPlayerClass( pl, "player_sandbox" )
BaseClass.PlayerSpawn( self, pl )
end[/code]
Which is why I am using the self.BaseClass method instead of the method used in sandbox. Because the DEFINE_BASECLASS step at first glance seems unnecessary, but I'm still wondering it if actually increases performance.
[QUOTE=-FrozenFire-;44682462]It's funny you say that because I'm doing it the exact same way as sandbox minus the DEFINE_BASECLASS.
[code]---- Make BaseClass available
--
DEFINE_BASECLASS( "gamemode_base" )
--[[---------------------------------------------------------
Name: gamemode:PlayerSpawn( )
Desc: Called when a player spawns
-----------------------------------------------------------]]
function GM:PlayerSpawn( pl )
player_manager.SetPlayerClass( pl, "player_sandbox" )
BaseClass.PlayerSpawn( self, pl )
end[/code]
Which is why I am using the self.BaseClass method instead of the method used in sandbox. Because the DEFINE_BASECLASS step at first glance seems unnecessary, but I'm still wondering it if actually increases performance.[/QUOTE]
Sorry for crapping up the thread, I didn't realise you were asking about BaseClass vs self.BaseClass . I was under the impression BaseClass didn't exist at all and you had to use DEFINE_BASECLASS to make self.BaseClass work.
Sorry, you need to Log In to post a reply to this thread.