Hi, I'm Marlon Etheredge, game researcher at the Computer Graphics Group of Delft University of Technology, in The Netherlands.
I'm currently working on a project to bring adaptive interactions to game worlds, as a casy-study we chose to use OpenClonk due to it's large set of interactions and it being open source.
We're already quite far in this project and have created a modified version of the OpenClonk server to run dedicated and log interactions, the basic idea behind this is as follows:
1) For a player in a game, log every action (e.g. Player shoots Gun, or, Player uses Shovel, or, Player kills OtherPlayer)
2) For every logged action, determine it's score according to an initial value (which is usually the same for every action) and a time-based decay (newer, and thus, more frequent actions should have a high score)
3) Store every score value in a matrix with rows as player sessions and columns as action scores
4) Run cluster-analysis to find clusters, or, styles of play
5) Train Hidden Markov Models to use them for classification (Baum-Welch and Viterbi)
6) Classify a player according to it's behavior in a game, and adapt to this behavior (e.g. by using a game design editor, for OpenClonk we'll be using Entika, introduced here)
(More details are described in this paper)
The way I've currently implemented this is by hooking into both the C4Object and C4Effect (SetAction and New), which, as far as I can see now with the little data we collected, seems to cover all actions (at least the actions that we're interested in).
An Update call is made to update the scores of all actions continuously.
I was wondering as to what your suggestions would be to further enhance the collection of interactions in OpenClonk.
I'm currently working on a project to bring adaptive interactions to game worlds, as a casy-study we chose to use OpenClonk due to it's large set of interactions and it being open source.
We're already quite far in this project and have created a modified version of the OpenClonk server to run dedicated and log interactions, the basic idea behind this is as follows:
1) For a player in a game, log every action (e.g. Player shoots Gun, or, Player uses Shovel, or, Player kills OtherPlayer)
2) For every logged action, determine it's score according to an initial value (which is usually the same for every action) and a time-based decay (newer, and thus, more frequent actions should have a high score)
3) Store every score value in a matrix with rows as player sessions and columns as action scores
4) Run cluster-analysis to find clusters, or, styles of play
5) Train Hidden Markov Models to use them for classification (Baum-Welch and Viterbi)
6) Classify a player according to it's behavior in a game, and adapt to this behavior (e.g. by using a game design editor, for OpenClonk we'll be using Entika, introduced here)
(More details are described in this paper)
The way I've currently implemented this is by hooking into both the C4Object and C4Effect (SetAction and New), which, as far as I can see now with the little data we collected, seems to cover all actions (at least the actions that we're interested in).
An Update call is made to update the scores of all actions continuously.
I was wondering as to what your suggestions would be to further enhance the collection of interactions in OpenClonk.
From scanning through it I saw that you are (were?) using the commands (see OC clustering findings, for example "Follow", "Dig", "MoveTo" etc). Those are not used at the moment! And if they are, only very rarely. They belong to the old pathfinder (in Clonk Rage you could send your Clonks to do stuff using your mouse) and in OpenClonk most of the stuff is done solely through the scripts.
What might yield a good result would be to hook into the script execution and catch the "call" events to filter out when "OnUse" is called in an item since that is a very common entry point for actions (using Axe, Shovel, shooting arrows, etc..)
What might yield a good result would be to hook into the script execution and catch the "call" events to filter out when "OnUse" is called in an item since that is a very common entry point for actions (using Axe, Shovel, shooting arrows, etc..)
One question I have on the amount of data you need for a trustworthy analysis, do you know this? What magnitude can we think of, like the order of 100 games?
And another point is difference between games, for examples in some of the races a player would use one item only (example Boompack, or wind bag) whereas in other rounds the whole spectra of weapons we currently have. Don't you think this will somehow mess up these player profiles you are talking about? Also the difference in playing styles between melees and settlement rounds is really large, how do you plan to capture this?
And another point is difference between games, for examples in some of the races a player would use one item only (example Boompack, or wind bag) whereas in other rounds the whole spectra of weapons we currently have. Don't you think this will somehow mess up these player profiles you are talking about? Also the difference in playing styles between melees and settlement rounds is really large, how do you plan to capture this?
I wrote some thoughts as comments into your blog. Basically, I think you should run it as some kind of contest to attract players. I'm not sure if you can see them; some "hack protection" wouldn't let me post words like "function" or "script".
Anyway, good luck with the project :)
Anyway, good luck with the project :)
We got a crash again, host timed out, after playing 5 minutes of GoldRush with two people.
@Maikel: Resolved the last crash, waiting for a server administrator currently...
@Maikel: About the trustworthy analysis, we have seen that in previous experiments apart from OpenClonk there was a tipping point at around 60 play sessions were we could see clusters (play styles) form in our analysis. Since OpenClonk features more complex data, this point with OpenClonk will yield more sessions. We're monitoring the data that we collect and run our analysis manually in the beginning, this to allow us to evaluate the results of our analysis.
@Sven2: Sorry about the hack protection, I've seen your comments, it seems that we currently cover every action that we're interested in, I like the idea of setting up some sort of competion but due to time constraints we're (unfortunately) not able to execute this :-(
@Zapper: The current implementation hooks into C4Object and C4Effect and intercepts SetAction, which seem to cover the C4Action/C4Object part of the actions, C4Effect interception in New seems to cover the rest
@Maikel: About the trustworthy analysis, we have seen that in previous experiments apart from OpenClonk there was a tipping point at around 60 play sessions were we could see clusters (play styles) form in our analysis. Since OpenClonk features more complex data, this point with OpenClonk will yield more sessions. We're monitoring the data that we collect and run our analysis manually in the beginning, this to allow us to evaluate the results of our analysis.
@Sven2: Sorry about the hack protection, I've seen your comments, it seems that we currently cover every action that we're interested in, I like the idea of setting up some sort of competion but due to time constraints we're (unfortunately) not able to execute this :-(
@Zapper: The current implementation hooks into C4Object and C4Effect and intercepts SetAction, which seem to cover the C4Action/C4Object part of the actions, C4Effect interception in New seems to cover the rest
Excuse me for the late response, I don't receive notifications upon replies.
>@Zapper: The current implementation hooks into C4Object and C4Effect and intercepts SetAction, which seem to cover the C4Action/C4Object part of the actions, C4Effect interception in New seems to cover the rest
This is more because most of the usage events ingame use some sort of C4Effect for particles, timing, etc.. However, ALL use the OnUse callback.
But you are right, since most actions ingame involve some sort of timer/graphical effect, catching C4Effect might cover a good amount of the cases
> I like the idea of setting up some sort of competion but due to time constraints we're (unfortunately) not able to execute this :-(
We will probably have competitions once we have the league server set up anyway. The game already supports all the mechanisms inherited from Clonk Rage. It's just that we don't have the server backend set up for OpenClonk yet.
Some more notes to ofx:
The league server automatically receives records of all rounds that are played (independent from the server!!) for cheat-protection purposes. A custom-built engine (like yours) could replay ALL of the records with the hooks in the right places and analyze the data. This would make you not depend on people playing games on your server.
The league server automatically receives records of all rounds that are played (independent from the server!!) for cheat-protection purposes. A custom-built engine (like yours) could replay ALL of the records with the hooks in the right places and analyze the data. This would make you not depend on people playing games on your server.
Wow, that sounds great! How may I use this? Again, we're on a tight schedule :(
If you are on a tight schedule, probably not at all because we have no league yet.
The league source code is still closed source and we are waiting for it to be put under a license that allows us to use it. Only after that, we can start to incorporate the league into our website, and get it running which will also take some time. Count with not less than a few months.
The league source code is still closed source and we are waiting for it to be put under a license that allows us to use it. Only after that, we can start to incorporate the league into our website, and get it running which will also take some time. Count with not less than a few months.
Could someone explain to me how I can best change properties of an object in OpenClonk, e.g. I want to change the rope length of a rope gun? (In C++)
I'm working on the binding with out semantics editor.
I'm working on the binding with out semantics editor.
All those properties are defined in script. If you want to change the rope gun, the easiest way would be to copy the rope gun definition (Objects.ocd\Items.ocd\Tools.ocd\GrappleBow.ocd) into your scenario (e.g. MyScenario.ocs\GrappleBow.ocd) and modify the value(s). Definitions copied into the scenario will always overload those defined in the standard package and they will be sent to all clients in network when they load the scenario on join.
If you want to change them at runtime, to stay in sync with the clients who don't have your engine, you will have to send a CID_Script control packet that changes the values. E.g. define global variables in script in your local definition (e.g. static g_rope_length = 100;), use those in the scripts (SetMaxLength(g_rope_length)) and send modifications via network (::Control.DoInput(CID_Script, new C4ControlScript("g_rope_length=200", -1, true), CDT_Decide);).
If you want to change them at runtime, to stay in sync with the clients who don't have your engine, you will have to send a CID_Script control packet that changes the values. E.g. define global variables in script in your local definition (e.g. static g_rope_length = 100;), use those in the scripts (SetMaxLength(g_rope_length)) and send modifications via network (::Control.DoInput(CID_Script, new C4ControlScript("g_rope_length=200", -1, true), CDT_Decide);).
> ::Control.DoInput(CID_Script, new C4ControlScript("g_rope_length=200", -1, true), CDT_Decide);
by the way this doesn't work anymore with the recent changes I made, but it should still be fine for 5.3.3
Isn't there a way to change the properties at runtime for one player only (in single player)? I'm asking this because we want to alter objects (properties) for a player, to a certain profile (play style).
So:
1) We learn that OpenClonk has play styles S1 ... S5
3) A game designer defines that property rope_length for the RopeGun object for style S1 should be L
4) At runtime, we learn that player P1 shows S1 behavior, we match S1 as P1's style
5) Whenever P1 uses the RopeGun, the rope_length value will be L
(A very minimal example)
So, the implementation of SetMaxLength will be "::Control.DoInput(CID_Script, new C4ControlScript("g_rope_length=200", -1, true), CDT_Decide);" right?
So:
1) We learn that OpenClonk has play styles S1 ... S5
3) A game designer defines that property rope_length for the RopeGun object for style S1 should be L
4) At runtime, we learn that player P1 shows S1 behavior, we match S1 as P1's style
5) Whenever P1 uses the RopeGun, the rope_length value will be L
(A very minimal example)
So, the implementation of SetMaxLength will be "::Control.DoInput(CID_Script, new C4ControlScript("g_rope_length=200", -1, true), CDT_Decide);" right?
The "::Control.DoInput(CID_Script, new C4ControlScript("g_rope_length=200", -1, true, true), CDT_Decide);" (I check with 5.3.3, you need the extra parameter for fInternal) sends the script "g_rope_length=200" to all clients and executes it.
You can send anything that is valid C4Script. For example, you could make g_rope_length an array of player numbers and then modify only the indexed value.
Let's say you copied the GrappleBow into your scenario. You then modify the grapple bow rope script (GrappleBow.ocd\Rope.ocd\Script.c). The rope length is set in the Connect function:
Now GetRopeLengthForPlayer would be a function that queries a global variable:
To change these values from the engine, you could write a helper function:
Finally, you can call this function on all clients running this C++ code on the host:
I haven't tested this; e.g. I'm not sure if obj2->GetController() will get you the proper player number. You should check by logging this. You can use calls to Log like this:
Log stuff is written to the OpenClonk.log and displayed in the console. The engine counterpart is called "LogF" in C++ code and is called analogous to e.g. fprintf.
You can send anything that is valid C4Script. For example, you could make g_rope_length an array of player numbers and then modify only the indexed value.
Let's say you copied the GrappleBow into your scenario. You then modify the grapple bow rope script (GrappleBow.ocd\Rope.ocd\Script.c). The rope length is set in the Connect function:
public func Connect(object obj1, object obj2)
{
StartRopeConnect(obj1, obj2);
SetMaxLength(GetRopeLengthForPlayer(obj2->GetController()));
[... rest of the function]
}
Now GetRopeLengthForPlayer would be a function that queries a global variable:
static g_rope_lengths;
func GetRopeLengthForPlayer(int plr)
{
// Default length for non-player hooks (e.g. AI clonks)
if (plr<0) return 100;
// init array on first call
if (!g_rope_lengths) g_rope_lengths = [];
if (!g_rope_lengths[plr]) g_rope_lengths[plr] = 100; // default length
// return current rope length for player
return g_rope_lengths[plr];
}
To change these values from the engine, you could write a helper function:
global func SetRopeLengthForPlayer(int plr, int new_length)
{
// init array on first call
if (!g_rope_lengths) g_rope_lengths = [];
// store new length
g_rope_lengths[plr] = new_length;
return true;
}
Finally, you can call this function on all clients running this C++ code on the host:
::Control.DoInput(CID_Script, new C4ControlScript(FormatString("SetRopeLengthForPlayer(%d,%d)", player_number, new_rope_length).getData(), -1, true), CDT_Decide);
I haven't tested this; e.g. I'm not sure if obj2->GetController() will get you the proper player number. You should check by logging this. You can use calls to Log like this:
Log("Current rope length array is: %v", g_rope_lengths);
Log("Variable plr is: %v", plr);
Log stuff is written to the OpenClonk.log and displayed in the console. The engine counterpart is called "LogF" in C++ code and is called analogous to e.g. fprintf.
Looking good, is there a way to do this for all properties (e.g. Set...(value) on an object, so I'm not required to write a C4Script function for every property I want to change)?
Sure, you could just pass an extra parameter to that function that controls which property to change. In fact, you could also send a script like "g_rope_lengths[plr] = new_length;" directly instead of the function call. The wrapper function is just there to make sure the array is already initialized. You could do that on scenario initialization to get rid of that step.
Switched from static const variables (e.g. Axe axe_swing_time) to static variables (since changing them would require non-const right?) but the variable seems to be ignored in script now. Any solution?
That depends on how you use them. No script errors are mentioned in the log?
Static const is replaced by its value at compile time; static variables will be used at runtime. You run into trouble if you try to use them e.g. in ActMaps, which are initialized at compile time. To change ActMap values at runtime, you would have to create a copy of the ActMap and then change it in the object instance.
Static const is replaced by its value at compile time; static variables will be used at runtime. You run into trouble if you try to use them e.g. in ActMaps, which are initialized at compile time. To change ActMap values at runtime, you would have to create a copy of the ActMap and then change it in the object instance.
Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill