So I wanna have cvars in my C++ engine, but then some problems arise:
[list]
[*] You can't see variable names as they are changed to asm, I assume you have to use std::map with a string as a key (that'll be cvar name), and then use something like this to add them:
[code]void addCvar(const int &variable, std::string name);[/code]
[*] Should the cvars be pointers to other variables in my code, or should other code access cvars in my cvar class?
[*] If they are pointers, I'd like to have only 1 std::map, so the pointer must be void*, but then, since I wanna have different types of cvars, how do I know what type of variable the pointer is pointing to?
[*] If they are stored in cvar class, wouldn't the log(n) std::map searching for cvars be too slow, since they will be searched often? While if they were pointers they would be only have to be searched while changing them.
[*] And then, since I don't know variable type, what should I do when user types cv_someinteger="asd" or cv_somestring=5?
[*] I've seen open source cvar project somewhere but I can't find it anymore, does anyone have a link?
[/list]
Facepunch coders! Help!
[QUOTE=Ritave;19273525]Should the cvars be pointers to other variables in my code, or should other code access cvars in my cvar class?[/QUOTE]
Both work.
[QUOTE=Ritave;19273525]If they are pointers, I'd like to have only 1 std::map, so the pointer must be void*, but then, since I wanna have different types of cvars, how do I know what type of variable the pointer is pointing to?[/QUOTE]
You just need an integer telling you, of which type the void* is.
If you are okay with using Boost, look up boost::variant and boost::any.
[QUOTE=Ritave;19273525]If they are stored in cvar class, wouldn't the log(n) std::map searching for cvars be too slow, since they will be searched often? While if they were pointers they would be only have to be searched while changing them.[/QUOTE]
I don't understand this. But you can sort your container or quicker searches :)
[QUOTE=Ritave;19273525]And then, since I don't know variable type, what should I do when user types cv_someinteger="asd" or cv_somestring=5?[/QUOTE]
Tell the user that he can't do it or just assign default value, e.g. sv_someinterger would be 0 if you wanna get an integer out of it. The second one would be okay, it's just "5".
[QUOTE=Ritave;19273525]I've seen open source cvar project somewhere but I can't find it anymore, does anyone have a link?[/QUOTE]
The Quake 3-code is GPL'ed.
Oh wow, quite genius, never would have fought that way!
Oh and since I don't really know windows library, what is that BOOTIL_EXPORT?
Bootil is part of my engine, no need to worry about that.
With that method should I say store the CVar value into a local variable for a class (say, scale of an entity):
[cpp]Entity() {this->m_Scale_local = m_Scale_console.GetFloat();}[/cpp]
or just continually call m_Scale_console.GetFloat() in my update function?
In that case, I think it would be best to have a callback whenever the particular cvar is changed.
[QUOTE=garry;19273822][cpp]
code[/cpp][/QUOTE]
So I was thinking, why do you have the Find & Get functions, instead of FindInt("var") couldn't you just do Find("var").GetInt() or something?
Just for the hell of it I'll post my implementation of a console+convar system too.
[code]
Global ConsoleOutHook:Int = AllocHookId()
Type Console
Global ConVars:TMap = New TMap
Global ConCommands:TMap=New TMap
Global Lines:TList = New TList
Global History:TList = New TList
Function AddVar(name:String , v:ConVar)
ConVars.Insert(name,v)
EndFunction
Function GetVar:ConVar(name:String)
Return ConVar(ConVars.ValueForKey(name))
EndFunction
Function AddCommand(name:String, v:ConCommand)
ConCommands.Insert(name, v)
End Function
Function GetCommand:ConCommand(name:String)
Return ConCommand(ConCommands.ValueForKey(name))
End Function
Function WriteLine(line:String)
RunHooks(ConsoleOutHook, line)
Lines.AddLast(line)
If Lines.Count()>128 Then
Lines.RemoveFirst()
EndIf
Print line
EndFunction
Function ProcessInput(in:String, silent:Byte = False)
If in.Length = 0 Then Return
History.AddLast(in)
Local a:String[] = in.Split(" ")
If Not silent Then
WriteLine(">> " + in)
EndIf
If a.Dimensions()[0] = 1 Then
' The user has entered a single command
If ConCommands.Contains(in) Then
' A concommand with this name exists.
GetCommand(in).Run(Null)
Return
EndIf
If ConVars.Contains(in) then
' A variable with this name exists.
Local v:ConVar = GetVar(in)
WriteLine(in+": "+v.Description+" Value: "+v.GetString()+" (Def. "+v.Def+")")
return
endif
endif
If a.Dimensions()[0] > 1 Then
If ConCommands.Contains(a[0]) Then
' A concommand with this name exists.
GetCommand(a[0]).Run(Right(in, in.Length - (a[0].Length + 1)).Split(" "))
Return
EndIf
If ConVars.Contains(a[0]) Then
' A variable with this name exists.
Local v:ConVar = GetVar(a[0])
v.SetString(Right(in, in.Length - (a[0].Length + 1)))
'WriteLine(a[0] + " set to " + Right(in, in.Length - (a[0].Length + 1)))
Return
endif
endif
WriteLine("Unknown command '"+in+"'.")
EndFunction
Function Clear(s:String[] = null)
Lines.Clear()
EndFunction
EndType
Type ConVar
Field Description:String
Field Value:String
Field Def:String
Method GetString:String()
Return Value
EndMethod
Method GetByte:Byte()
Return Byte(Value)
EndMethod
Method GetInt:Int()
Return Int(Value)
EndMethod
Method GetFloat:Float()
Return Float(Value)
End Method
Method SetInt(v:Int)
Value=String(v)
EndMethod
Method SetFloat(v:Float)
Value = String(v)
EndMethod
Method SetString(v:String)
Value=v
EndMethod
Function Create:ConVar(name:String, Desc:String, def:String = "0")
Local c:ConVar = New ConVar
c.Description = Desc
c.Value = def
c.Def=def
Console.AddVar(name , c)
Return c
EndFunction
EndType
Type ConCommand
Field Func:Int(args:String[])
Method Run(args:String[])
Func(args)
EndMethod
Function Create:ConCommand(name:String, f:Int(args:String[]))
Local c:ConCommand = New ConCommand
c.Func = f
Console.AddCommand(name, c)
Return c
End Function
Function MapTo(a:String, b:String)
Console.AddCommand(a, Console.GetCommand(b))
End Function
End Type
Function ExecFiles:Int(a:String[])
If Not a Then
Console.WriteLine("Specify file(s) to run.")
Else
For Local s:String = EachIn a
Local f:TStream = OpenStream("Resource/" + s)
If f Then
While Not f.Eof()
Console.ProcessInput(f.ReadLine())
EndWhile
Else
Console.WriteLine("File " + s + " not found.")
EndIf
Next
Return 0
End If
End Function
ConCommand.Create("exec", ExecFiles)
[/code]
There's some oddities here because of language limitations but it works good.
You declare a variable:
Global cc_wireframe:ConVar = convar.Create("r_wireframe", "Wireframe rendering", "0")
Then you can access it:
[code]
If cc_wireframe.GetByte() Then
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
Else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
End If
[/code]
The concommands work differently:
[code]
Function PissBalls:Int(a:String[])
If a Then
TestShader = LoadShader("Resource/Shader/" + a[0] + ".frag")
End If
End Function
ConCommand.Create("shader", PissBalls)
[/code]
Still, works as I wanted and if I were going to port it to another language it'd work the same way. I also implemented key bindings with this system rather shittily if anyone cares:
[code]
' Key bindings!
Global BKEY_FORWARD:Int = 0
Global BKEY_BACKWARD:Int = 0
Global BKEY_LEFT:Int = 0
Global BKEY_RIGHT:Int = 0
Global KEYBINDS:TMap = New TMap
Global Special_Keys:TMap = New TMap
Special_Keys.Insert("tab", String(KEY_TAB))
Special_Keys.Insert("space", String(KEY_SPACE))
Special_Keys.Insert("return", String(KEY_RETURN))
Special_Keys.Insert("enter", String(KEY_ENTER))
Special_Keys.Insert("alt", String(KEY_LALT))
Special_Keys.Insert("ctrl", String(KEY_LCONTROL))
Special_Keys.Insert("shift", String(KEY_LSHIFT))
Special_Keys.Insert("backspace", String(KEY_BACKSPACE))
Special_Keys.Insert("ins", String(KEY_INSERT))
Special_Keys.Insert("insert", String(KEY_INSERT))
Special_Keys.Insert("del", String(KEY_DELETE))
Special_Keys.Insert("deletr", String(KEY_DELETE))
Special_Keys.Insert("home", String(KEY_HOME))
Special_Keys.Insert("end", String(KEY_END))
Special_Keys.Insert("left", String(KEY_LEFT))
Special_Keys.Insert("right", String(KEY_RIGHT))
Special_Keys.Insert("up", String(KEY_UP))
Special_Keys.Insert("down", String(KEY_DOWN))
Special_Keys.Insert("tilde", "96")
Special_Keys.Insert("`", "96")
Function BindKey:Int(a:String[])
If a And a.Dimensions()[0] > 1 Then
Local keynum:Int
If a[0].Length = 1 Then
keynum = Asc(Upper(a[0]))
ElseIf Special_Keys.Contains(Lower(a[0])) Then
keynum = Int(String(Special_Keys.ValueForKey(Lower(a[0]))))
Else
console.WriteLine("Must bind a valid key!")
Return 0
EndIf
Select a[1]
Case "forward"
BKEY_FORWARD = keynum
Case "backward"
BKEY_BACKWARD = keynum
Case "left"
BKEY_LEFT = keynum
Case "right"
BKEY_RIGHT = keynum
Default
Local l:Int = a.Dimensions()[0]
Local build:String = ""
For Local i:Int = 1 To l - 1
build = build + a[i] + " "
Next
KEYBINDS.Insert(String(keynum), build)
End Select
Else
Console.WriteLine("Format: bind <key> <command>")
EndIf
End Function
concommand.Create("bind", BindKey)
Function RunKeyBindings()
For Local key:String = EachIn KEYBINDS.Keys()
If KeyHit(Int(key)) Then
Console.ProcessInput(String(KEYBINDS.ValueForKey(key)), True)
End If
Next
End Function
[/code]
Sorry, you need to Log In to post a reply to this thread.