Interest in map patcher mutator?

Search, find and discuss about Mutators!
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Interest in map patcher mutator?

Post by Barbie »

Is there interest in a mutator that modifies Actors while a map is loaded? In my imagination you define the map name and all the patches that should be applied in an INI file; once the map patcher mutator is loaded, it will read that INI file, check if a patch list is given for this map and do the tweaks then. (I talked about such here already.
An advantage would be that code and data will be divorced.

Some examples:
  • correcting mismatching settings for Monsters (Meshes, Orders, RangedProjectile, Health, ReducedDamageType/Pct, AnimSequence)
  • replacing Actors (Weapons)
  • moving Actors (Trigger, items placed half in the void)
  • spawning additional items (mostly Inventory, but also Counter, Teleporters, ...)
  • adjusting "fantastic" Thing-/CreatureFactory-settings to realistic values (MaxItems, Interval, Capacity, Prototype, bCovert)
  • adjusting Mover properties (MoveTime, MoverEncroachType, DelayTime, InitialState, bTriggerOnceOnly, StayOpenTime)
  • adjusting Collision Cylinder
  • removing Teleporter's URL and spawning a MHEnd there
  • getting rid of broken Acors by deleting them ("JumpDetectorVX")
  • force Actors to a certain State
  • raising Events when all Actors have been initialized
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
MrLoathsome
Inhuman
Posts: 958
Joined: Wed Mar 31, 2010 9:02 pm
Personal rank: I am quite rank.
Location: MrLoathsome fell out of the world!

Re: Interest in map patcher mutator?

Post by MrLoathsome »

That does sound very interesting. In a number of ways.

I have messed with a number of ways to try and "fix" things before.
The idea of attacking the maps themselves via mutator has crossed my mind a few
times. But since I have pretty much avoided any mapping so I could focus on code/content, it has
been low on the list.

Somebody else who knows mapping better than I should undertake this task. :roll:

I nominate Barbie. :tu:

It would be tricky I think. Ideally it should work with every map type and gametype out there.
And also not screw with/over-ride any changes made by any other mutators or whatever added to the game after the level loads.

It should just attack maps at startup, then bail. (You probably know all that, but just some thoughts....)
blarg
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Interest in map patcher mutator?

Post by sektor2111 »

Barbie wrote:Is there interest in a mutator that modifies Actors while a map is loaded?
YES, why Not ?
If can address correctly certain actors from map to perform adjustments, removals, then it's good. I should be done for a couple of years but no one bothered. However such a setup needs time but it might worth efforts.
Spectra
Masterful
Posts: 542
Joined: Tue Jan 22, 2013 5:23 pm
Personal rank: Nullified!
Location: (X) Unable To Locate....

Re: Interest in map patcher mutator?

Post by Spectra »

I already tried to make one.

Yet I will re-post the same thing here:

Code: Select all

class AnyTweakingActor extends Actor config(MBotv2Config);

enum EClassAction
{
	CA_Tweak,
	CA_Destroy
};

struct Anytwk
{
	var() config string MapTitle;
	var() config name ActorName;
	var() config name ActorState;
	var() config EClassAction ClassAction;
	var() config String ActorSettings;
};

var() config Anytwk AnyTweaker[128];

event PostBeginPlay()
{
	TweakSomething();
	SaveConfig();

	SetTimer(5.0, false);
}

simulated function TweakSomething()
{
	local int i;
	local Actor A;
	local int Pos;
	local string CSettings;

	for(i = 0; i < 127; i++)
	{
		if(Level.Title == AnyTweaker[i].MapTitle)
		{
			if(AnyTweaker[i].ClassAction == CA_Tweak)
			{
				ForEach AllActors(Class'Actor', A)
				{
					if( (AnyTweaker[i].ActorName != '') && A.Name == AnyTweaker[i].ActorName)
					{
						if(A != None && AnyTweaker[i].ActorState != '')
							A.GotoState(AnyTweaker[i].ActorState);
						
						if(A != None && AnyTweaker[i].ActorSettings != "")
						{
							CSettings = AnyTweaker[i].ActorSettings;
							T1H:
							if(len(CSettings) > 0)
							{
								Pos = InStr(CSettings,";");
								if(Pos < 0)
									Pos = InStr(CSettings,",");
								if(Pos < 0)
								{
									SetActorSettings(A,CSettings);
									CSettings = "";
								}
								else
								{
									SetActorSettings(A, Left(CSettings,Pos));
									CSettings=Mid(CSettings,Pos + 1);
									goto T1H;
								}
							}
						}
					}
				}
			}
			else if(AnyTweaker[i].ClassAction == CA_Destroy)
			{
				ForEach AllActors(Class'Actor', A)
				{
					if( (AnyTweaker[i].ActorName != '') && A.Name == AnyTweaker[i].ActorName)
					{
						if(A != None)
						{
							A.Destroy();
							A = None;
						}
					}
				}
			}
		}
	}
}

simulated function SetActorSettings(Actor A, String CSettings)
{
	local string Property;
	local string Value;
	local string Prev;
	local string Next;

	Property=Left(CSettings,InStr(CSettings,"="));
	Value=Mid(CSettings,InStr(CSettings,"=") + 1);

	if(A != None)
	{
		Prev=A.GetPropertyText(Property);
		A.SetPropertyText(Property,Value);
		Next=A.GetPropertyText(Property);
	}
}

simulated function Timer()
{
	Destroy();
}

defaultproperties
{
	RemoteRole=ROLE_SimulatedProxy
}

Code: Select all

AnyTweaker[0]=(MapTitle="MH-LandsOfNapali",ActorName=Mover34,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[1]=(MapTitle="MH-LandsOfNapali",ActorName=Mover35,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[2]=(MapTitle="MH-LandsOfNapali",ActorName=Mover45,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[3]=(MapTitle="MH-LandsOfNapali",ActorName=Mover46,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[4]=(MapTitle="MH-LandsOfNapali",ActorName=Mover52,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[5]=(MapTitle="MH-LandsOfNapali",ActorName=Mover53,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[6]=(MapTitle="MH-LandsOfNapali",ActorName=Mover32,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[7]=(MapTitle="MH-LandsOfNapali",ActorName=Mover33,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[8]=(MapTitle="MH-LandsOfNapali",ActorName=Mover44,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[9]=(MapTitle="MH-LandsOfNapali",ActorName=Mover43,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[10]=(MapTitle="MH-LandsOfNapali",ActorName=Mover51,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[11]=(MapTitle="MH-LandsOfNapali",ActorName=Mover27,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[12]=(MapTitle="MH-LandsOfNapali",ActorName=Mover40,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[13]=(MapTitle="MH-LandsOfNapali",ActorName=Mover41,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[14]=(MapTitle="MH-LandsOfNapali",ActorName=Mover39,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[15]=(MapTitle="MH-LandsOfNapali",ActorName=Mover55,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[16]=(MapTitle="MH-LandsOfNapali",ActorName=Mover36,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[17]=(MapTitle="MH-LandsOfNapali",ActorName=Mover42,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[18]=(MapTitle="MH-LandsOfNapali",ActorName=Mover28,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[19]=(MapTitle="MH-LandsOfNapali",ActorName=Mover10,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[20]=(MapTitle="MH-LandsOfNapali",ActorName=Mover8,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[21]=(MapTitle="MH-LandsOfNapali",ActorName=Mover9,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[22]=(MapTitle="MH-LandsOfNapali",ActorName=Mover7,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[23]=(MapTitle="MH-LandsOfNapali",ActorName=Mover13,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[24]=(MapTitle="MH-LandsOfNapali",ActorName=Mover17,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
AnyTweaker[25]=(MapTitle="MH-LandsOfNapali",ActorName=Mover18,ActorState=TriggerControl,ClassAction=CA_Tweak,ActorSettings="")
The example above is MH-LandsOfNaPali, where bots won't stuck at doors.
The code is not complete and it supports max upto 128 settings. Timer thing can be removed as Lifespan will do the same thing.
I know Level.Title is not correct method. Instead the string should have actual map file name and not map title.

Still if you guys want to make your own one, then count me in too.
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Interest in map patcher mutator?

Post by Barbie »

MrLoathsome wrote:But since I have pretty much avoided any mapping
I'm afraid you have to use the UnrealEd to feed that mutator: you'll need the unique name of the Actor (e.g. »CreatureFactory3«) to set its properties. But of course you could also export the map to a T3D file and grab the name there with a text editor. :)
MrLoathsome wrote:Ideally it should work with every map type and gametype out there.
Yes, there should be no dependencies. But friction points with other Actor replacing mutators will be unavoidable - the only solution I see is a combination of Spawn() and AlwaysKeep() to fetch the created Actor. Also "late changing" properties at a time when all Actors have been initialized may be a problem if that Actor has been replaced by another Mutator.
Rocky wrote:I already tried to make one.
In my imagination that mutator will process a byte code given by the ini file. Due to the INI restrictions (max. line length=1023, no "dangerous" characters like line break, null, etc.) I have to think about the format of that byte code - maybe base64 coded? But I should start with the definition of the opcodes...
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Interest in map patcher mutator?

Post by Barbie »

I made some thoughts of the data handling for this job: Because file access is very limited in UScript, I want to put the map file names, used strings and commands to execute into an INI file like this:
Spoiler

Code: Select all

[SBMapPatcherV0.SBMapPatcher]

LogLevel=LOG_Debug
bActive=true

Maps[0]=MH-Gate-X
Maps[1]=
Maps[2]=
Maps[3]=

Strings[0]=Mover
Strings[1]=lightson
Strings[2]=Tag
Strings[3]=Trigger
Strings[4]=DoorToPortal
Strings[5]=TriggerDoorToPortal
Strings[6]=bInitiallyActive
Strings[7]=false
Strings[8]=Event
Strings[9]=MoverEncroachType
Strings[10]=ME_CrushWhenEncroach
Strings[11]=TriggerOpenTimed
Strings[12]=StayOpenTime
Strings[13]=Just a test


Cmds[0]=KgMBXJFA
Cmds[1]=
Cmds[2]=
Cmds[3]=
Maps is just a list if the file names for maps to be tweaked.
Strings is a collection of all strings that occur over all map patches.
Cmds is the sequence of commands and their parameters to be executed on Maps in base64 coding format. It will be transformed into an byte array for a specific map before fed to the parser.

I think an example explains it best:
in map "MH-Gate-X" (index 0) the Collision Size for Actor "Trigger1" should be changed to (92, 64). My internal opcode for "SetCollisionSize" is 42, its parameter count is exact 3 and the data types are Name/integer/integer (Actor Name/Radus/Height). The representation in UScript after finding that Actor:

Code: Select all

Actor.SetCollisionSize(92, 64);
My command sequence in bytes:
42 (command "SetCollisionSize")
3 (compact integer into the Strings array, what results in "Trigger" here)
1 (compact integer of the number of that actor, what results in 1 here)
Now the name of the actor "Trigger1" is known, proceed to get other command parameters:
92 (compact integer of CollisionRadius 92, part I)
1 (compact integer of CollisionRadius 92, part II)
64 (compact integer of CollisionHeight 64)
So the full command sequence would be "42|3|1|92|1|64" or base64-encoded for INI file: "KgMBXJFA". Because of the 1023 char limit length of INI files and the 4/3 inflation by base64 there would fit 1023 / 4 * 3 = 767 bytes into a command string. Assuming an average of 6 bytes per command sequence, ~130 commands could be applied to a map what seems to be enough.

Of course I have to provide a GUI for creating that INI file; no one wants to waste time by bit shifting and base64 coding manually.

Any further thoughts or suggestions? :mrgreen:
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Interest in map patcher mutator?

Post by sektor2111 »

Barbie wrote:Any further thoughts or suggestions?
Yes, if you don't mind.
I was thinking at some Actor/(slash)Mutator or such. This one might be prioritized spawned by GameType before even BaseMutator. How do I bring patching in stage ? Higor has some sample in that XCGE last 19 with pathing stuff in run-time. But... I'm thinking a bit different. MapName string get some Data here Excluding .Extension and Prefix MH- using simple string operations (if you like them) and attaching a suffix "_PTM". Then dynamicload "MapName_PTM.A_Patcher", MightFail = True (in case that you don't need patch for certain map - doubts here). Packages MapName_PTM are loaded if they do exist and firing internals as follows:
- an unique iterator checking Actors using a Case and Name then setup required;
- tiny logs when job starts and it's done;
- other log if patcher module could not be found.

Common cons:
** Each map created will require a check and a... list with things. XCGE helps even in adding a required packagemap in runtime - I did this at NsUTw. For sure patcher being addressed to a map won't very need a bunch of changes and... Server-Side things won't mismatch nothing, you can update them as needed. I don't see any reason to have 10 MB of INI file to read or much over 1023 chars since patcher can be a file matching map-name loaded and fired at job before even to load anything game-related - LIKE I said checkout BotzMutator how do works, it loads modules based on Map-Name not a giant INI file and preventing dependencies.
** Creating these modules is a good job but extremely time-consuming - me, one I feel tired of MH tweaking, I wanna play the game, I want to train myself in cubing spree (mapping), and MH with a lot of "maps" will require years of patching thanks to **mappers** clueless what they did. Also things to be configured in an INI are not easy for non-experts admins. I go for such a simple setup not for a complex formula letting people in fog with SQL and reading bytes, files, and drives, and bla bla sucking life from your blood. This way will require actor/mutator in INI and then it will load modules when is necessary. If a module map related is found added, then SPAWN it and leave it to do its job. Updates are gonna be easy this way. Also Take a look at drip fixing by Higor - it can be changed as needed.
** If we will see multiple options in a map we can create an INI on setup purpose.
** There are maps which cannot be patched that easy.
** For sure not only noob Admins won't be able to figure what you do - I hope to not be useless your bytes spree and affinity for making life harder than it needs.

Or maybe I have to start such thing in whatever holiday - but the question is how many maps worth such an effort, because I know some with the same BSP craps in even V_fix1044. Nothing changed but more trash added, and creating paths there is really time consuming + checking each lousy Mover and Trigger, hey, Editor can fix them forever...

This Patcher will require probably a WEB-Page where admins can find updates because updating will be simple: adding an U file in system - around a few seconds depending on mouse speed, no pain, no headache.
Main problem will be some genius telling me "fuck-face" when will find a patcher for his "map"/s - at least each of them will find how good they are in doing stuff, and how much work follows to their "work". Map + files +... patcher module from [*]http://UTMapPatches.Com[/*] :loool: , in order to make things functional. Yes, it's interesting...
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Interest in map patcher mutator?

Post by Higor »

Meh.

Code: Select all

perobjectproperties;
Now parsing the INI file doesn't take an awful lot of time.
Item Spawner that creates the rules object https://github.com/CacoFFF/SiegeIV-UT99 ... Spawner.uc
Rules object that can potentially read from different INI's https://github.com/CacoFFF/SiegeIV-UT99 ... erRules.uc
Siege statics, a collection of useful global functions: https://github.com/CacoFFF/SiegeIV-UT99 ... Statics.uc

The model works better like this:
- INI file: "MapPatcher.ini"

- Ini format:

Code: Select all

class ActorInfoObject expands Object
    perobjectconfig;

struct ActorInfo
{
    var() name ActorName;
    var() string Props; //prop=value?prop=value?...?prop=value
};

var ActorInfo Actors[64];

Code: Select all

local string MAPNAME_FIXED;
MAPNAME_FIXED = string(self.Outer.Name); //Does this even work?
ReplaceText(MAPNAME_FIXED,"[","_"$string(asc("[")) );
ReplaceText(MAPNAME_FIXED,"]","_"$string(asc("]")) );
ReplaceText(MAPNAME_FIXED,"|","_"$string(asc("|")) ); //Keep replacing non-alphanumeric chars

Code: Select all

[MAPNAME_FIXED]
Actors[0]=(ActorName=adsadasda,Props=prop=value?prop=value?...?prop=value)
Actors[1]=(ActorName=adsadasda,Props=prop=value?prop=value?...?prop=value)
...

Code: Select all

class DummyObject_MyPackageName expands Object
     perobjectconfig;

var ActorInfoObject AInfo;
//In the mutator...

Code: Select all

var DummyObject_MyPackageName IniDummy;
var name DummyName;

event PostBeginPlay()
{
   //Process MAPNAME_FIXED
   SetPropertyText("DummyName",MAPNAME_FIXED);
   IniDummy = new( self, 'MapPatcher') class'DummyObject_MyPackageName'; //This provides INI name
   IniDummy.AInfo = new( IniDummy, DummyName) class'ActorInfoObject'; //This autoloads the map entry of said INI
   //Process actors here
}




===========================================
===========================================
If you can't decode this, you have no business trying to make a mutator of this kind.
Have a fun read.
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Interest in map patcher mutator?

Post by sektor2111 »

Interesting stuff, Higor...
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Interest in map patcher mutator?

Post by Barbie »

Thanks for inspiration and suggestions.

The method using an INI file per map reduces the amount of data to be loaded , gives the possibility to put the patch instructions into a simple array and makes the patching more modular. On the other side it adds noise to the amount of INI files and I see the problem that a map file name is not always of data type NAME. I'll think about it...

Code: Select all

Actors[0]=(ActorName=adsadasda,Props=prop=value?prop=value?...?prop=value)
I doubt that the result of for example SetLocation() or SetCollisionSize() can be archived by SetPropertyText(). So extra commands beside SetProperty will be needed.
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
Aldebaran
Masterful
Posts: 672
Joined: Thu Jan 28, 2016 7:30 pm

Re: Interest in map patcher mutator?

Post by Aldebaran »

Perhaps this mutator could change the shown mapname in browserlist to a self-defined one?
Some maps shows wrong map titles or none...
Or is this a problem because different names are shown then perhaps for the same map?
User avatar
Barbie
Godlike
Posts: 2792
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Interest in map patcher mutator?

Post by Barbie »

Aldebaran wrote:Perhaps this mutator could change the shown mapname in browserlist to a self-defined one?
Some maps shows wrong map titles or none...
Do you mean the information that is shown on clients opening the server browser? Then I think that is a complete different work area: <MY GUESS>port 7778 of a server is used to gather information about that server: Admin name, current map name, player names, etc. These information is processed then by servers that offers ListFactories and distributed finally to requesting clients. Every link of this information processing chain may fail on unexpected transferred data, e.g. special chars in player or map name.</MY GUESS>

<EDIT>
Or do you just mean the value of LevelInfo.Title?
</EDIT>
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett
Aldebaran
Masterful
Posts: 672
Joined: Thu Jan 28, 2016 7:30 pm

Re: Interest in map patcher mutator?

Post by Aldebaran »

For example some map names are missing in UT Server Browser (look screenshot).
But also some names there are wrong, for example when playing map MH-Lordz-beta in the browser tab you find "MH-Breathe" as map name at this server position.
I thought it could be easy to set a correct map name in a config file which is shown instead of the wrong one.
Perhaps you are right and it is an other work area. Server information via ListFactories is perhaps updated before your mutator is activated.
Attachments
pic.jpg
pic.jpg (30.13 KiB) Viewed 3766 times
User avatar
papercoffee
Godlike
Posts: 10443
Joined: Wed Jul 15, 2009 11:36 am
Personal rank: coffee addicted !!!
Location: Cologne, the city with the big cathedral.
Contact:

Re: Interest in map patcher mutator?

Post by papercoffee »

Aldebaran wrote:For example some map names are missing in UT Server Browser (look screenshot).
But also some names there are wrong, for example when playing map MH-Lordz-beta in the browser tab you find "MH-Breathe" as map name at this server position.
I thought it could be easy to set a correct map name in a config file which is shown instead of the wrong one.
Perhaps you are right and it is an other work area. Server information via ListFactories is perhaps updated before your mutator is activated.
Open the maps in Editor and look into the Level-info. It could be that the server browser shows only the title inside the Level-info and not the file name.

This can happen when inexperienced mapper take a map as base for their MH map and have no clue that they have to change the Level-info title of the map as well... or don't know that they have to insert a title at all.
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Interest in map patcher mutator?

Post by sektor2111 »

Nobody will open anything because this means a new Level modified for 0 importance things. It's all about what MASTERSERVER sends to player if Level has no Title, the best option is sending Map-Name if Title returns an empty string - like for a modern MasterServer Setup, right ? Because those titles are in Browser, and data is completed from MasterServer not from server in cause.

Else, a mutator will solve this problem. All right, and then ? And then convince admins to read this topic and to use a mutator for "Titling Map" purpose... the most of them are asking things which already have answers due to their "not reading" usual habits.
Post Reply