| Home Page | Recent Changes | Preferences

Mychaeel/Developer Journal

Don't edit my journal entries, but feel free to add comments.

December 14, 2002

Read over your compositions, and wherever you meet with a passage which you think is particularly fine, strike it out.

Wikipedia logo Samuel Johnson

No effort is wasted if you've learned something from it. (Though that doesn't mean that the same time couldn't have been spent more effectively.)

Occasionally I'm stuck in a dead end of my own design and can't seem to find my way out of it. The way in there is plastered with a few truly elegant thoughts and implementations that I don't want to tread on. So I linger a bit longer and cudgel my brains for some other way to reach my goal than backing up.

Deleting an evening's worth of good code requires an effort, but if it relieves me of a week's worth of trying not to scratch it, it's worth it.

Comments

Tarquin: "The way in there is plastered with a few truly elegant thoughts and implementations that I don't want to tread on. " – I do that all the time in mapping. I end up with multiple copies of the same area, each one trying a different solution... What usually happens is I eventually work up the courage to start from scratch.

Mychaeel: That's exactly what happens to me too. :-) Realizing when it's better to scratch a few good ideas in favor of an entirely different approach (maybe even one with a slightly different outcome than what I originally figured) is a learning process.

December 20, 2002

Note to self: Not assuming that other people's contributions to a testing environment are free of bugs can be a huge time saver.

January 17, 2003

Incidentally I dug out the code I scratched in the first entry on this page and used it for a related, but different purpose where it actually fits in. (Good thing I never really delete something for good unless I'm absolutely sure it won't ever be of any use anymore.)

The code is part of Jailbreak's bot support and is supposed to let bots make an educated guess where an enemy player is currently located after the enemy gave his position away by triggering a release switch. Naturally, if there's only a single relase switch, all that boils down to a single possible location. Where it gets interesting is if there are multiple release switches and the bots actually have to guess which one the player triggered.

My code does that based on the last known location of a player (directly after a round starts, or after a spawn in freedom after winning an arena match, or after being released from jail), the walking distance (using the path network) to all of the release switches and the time that passed since the player's location was known last. With that and a more or less arbitrarily chosen probability distribution for a player's distance from his starting point after a given amount of time (something based on half a Gaussian) bots take a random guess weighed by the respective probability of the enemy being at any of the possible switches.

January 18, 2003

Wrote about A Bug's Life hoping that it'll inspire our bug hunters to posting better bug reports than they occasionally did for Jailbreak III.

January 23, 2003

UT2003's speech menu redefines the terms "non-extensible" and "hard-coded." I'm sure there's a way into it though – I just haven't worked it out yet. (Subclassing ExtendedConsole? is, hardly surprising, not an option.)

January 24, 2003

[speechmenuhack-ordermenu]

Apparently there is a way. The "Team tactics" menu item here opens a submenu where you can select one of a set of tactics for your team. (All other menu items open a list of players you can order.)

Getting it to display your own entries is the easy part, to be sure – being notified when the player selects one of the entries requires some more code insight. It is possible though, and I'm just about to implement it.

If this works out, I'll describe the hack in detail somewhere on the Wiki.

January 25, 2003

It works. When you select one of the entries in the tactics menu displayed to the right, SetOrders in TeamAI is notified and gets a chance to intercept the selection. The default implementation of SetOrders puts the following message into the log:

  ScriptLog: Team New orders TacticsNormal JB-IronMan.xPlayer0

TacticsNormal is a label I assigned to that menu item; everything else in that message is from Epic's own code. Any unwanted cosmetic side effects can be controlled by overwriting SetOrders with your own code.

There's relatively little code involved in the whole hack: One piece of code that's executed in some actor's client-side Tick (I used Tick in our HudBTeamDeathMatch? subclass, but it could be anywhere) that modifies the speech menu's behavior; and a piece of code that modifies some properties of Bot?s when they're spawned. And, of course, the code in SetOrders in TeamAI that does whatever you want your custom menu item to do.

Modifying the speech menu that way has its limitations, of course. There are only very few "slots" left for custom notifications (exactly nine), and custom submenus like my "Team Tactics" menu are only possible for menu items in the "Orders" submenu. Details to follow.

[speechmenuhack-tacticsmenu]

February 3, 2003

So little time, and so much to do... but I don't regret spending the last weekend skiing in Tyrol. ;-)

UMake should be updated and released in version 1.2. Spectate Botmatch has a host of new functionality to be implemented, despite Epic refitting a "Spectate" button in the Instant Action dialog with the last patch. Deathball needs some small code tweaks on my part. Jailbreak development must continue. And in addition to that there's some Perl coding work I'll actually get paid for and whose deadline is approaching sometime in mid-February.

February 10, 2003

If you want to make absolutely sure that a certain thread in a forum remains undiscovered, stick it to the top and place the words "Read this before posting" in its subject.

February 14, 2003

Bots in jail (in Jailbreak) should idly walk around, occasionally lie down and sleep, and engage in hammer fights if there's an eligible opponent.

It seems to me that maybe the best way to achieve all this is creating several custom ScriptedSequence subclasses, one for "walking around" (to select roam destinations in jail only), one for "lying down and sleeping" and one for "engage in jail fight (and stop when the opponent opts out)". The latter two will probably require custom scripted actions too: "start sleeping," "wait and occasionally snore" and "stand up"; and whatever is required to make a bot attack an opponent with an arbitrary melee or non-melee weapon.

Sounds like I'll better release alpha 4 before tackling that...

February 15, 2003

UT2003 bots don't really support being animated by means of a ScriptedSequence – even if you manage to make them play a certain animation, the xPawn? reverts to an idling pose as soon as that animation has ended. In addition, those idling animations are natively "physics controlled."

What's possible though is temporarily resetting the names of the idling animations to refer to non-existent animations. That's a pretty heavy-handed approach, but it works (and puts a bunch of warning messages into the log).

Update: ZappyAd pointed me to the bPhysicsAnimUpdate flag in class Pawn. Setting it to False disables the native "physics-controlled" animations, and indeed, it works well.

February 17, 2003

Scratched sleeping bots. Implemented bots that hammer-fight in jail with human players instead. Much more fun and rewarding.

February 24, 2003

I figured out the hard way that even custom game types can't simply replace the game's TeamInfo actors by subclassing xTeamRoster?. One of our bug hunters reported that setting up a game with custom bots broke crucial parts of the gameplay, and a look into the log revealed that UT2003 didn't use Jailbreak's custom TeamInfo subclass as configured in the the game type's DefaultEnemyRosterClass variable but instances of TeamBlueConfigured? and TeamRedConfigured? instead.

So, once more, I'm ending up attaching an actor of mine to a stock UT2003 actor instead of replacing the stock actor's class by my own. Jailbreak did so with several other gameplay-relevant classes (not just TeamInfo) before already – PlayerReplicationInfo, for one, instead of introducing a custom PlayerReplicationInfo subclass.

In Unreal Tournament "attaching" an actor to another actor was more or less limited to the possibility of setting the attached actor's owner to the actor it was attached to and then using the ChildActors iterator to retrieve the attached actor when requested. (Or any variant of that, though those that come into my mind are all equally inefficient.)

In UT2003 however every actor can have its own inventory by means of the Inventory variable which is declared in class Actor. Traveling an actor's inventory chain is fast and efficient compared to using an iterator, so I implemented a generic abstract superclass for info actors that are "attached" to another actor, including code that automatically sets up a linked list of all "actor tag" actors of the same class and a mechanism that, if so desired, communicates registration and unregistration of an "actor tag" to all clients even during its lifetime.

Well, anyway, the TeamInfo problem was quickly fixed thanks to that mechanism.

Comments

Wormbo: Actually the inventory chain was available to all actors in Unreal v205 already. (Yes, I really have that code here... :D)

Mychaeel: Hmm. I don't, but I do have the Unreal Tournament sources and... behold, you're right. Oh well. :-)

March 17, 2003

When I was implementing a new feature yesterday I noticed that I had to change a remotely related part of the code in order to avoid some small lag in a visual effect – no big deal, but something that deserved to be fixed. So I opened the class responsible for the delay, found the place where I had not anticipated the need for some direct connection between two pieces of code in my original design, and added it.

Then I realized that this was the third or fourth time I had added a bit of code at that place to accommodate a new requirement that had popped up since I had initially designed that class. Each time I had addressed a small particular problem, each time I had added a bit of code to fix it; and while each of those changes were small and simple, the whole code grew more complicated and less elegant with every change I applied to it.

Time to scratch that part of the code and rethink its design with all old and new requirements in mind.

Changing, fixing and tweaking code a small part at a time inevitably leads to bloated, unelegant, bug-prone constructions sooner or later. That's commonly called [feature creep] – and after a time any software suffering from it becomes so unmaintainable that adding, testing and debugging a new feature becomes almost as much work as rewriting everything from scratch. It's a developer's nightmare.

So sometimes it really pays off to start redesigning parts of a program before feature creep sets in. The difficult part is to realize that before it's too late; and even more so for people not directly involved in the development process who can't relate to the idea of elegant, straightforward and bug-resistant program design. Coding for a mod whose development is being slowly ground to a halt by somebody in charge adding more and more features that were never part of the original design can be a pretty frustrating experience.

May 4, 2003

ElapsedTime and RemainingTime in GameReplicationInfo aren't replicated all the time in UT and UT2003 – that's for saving bandwidth, I suppose, and that must be a good thing.

Bad thing is that it doesn't work too well. The idea is that ElapsedTime and RemainingTime are replicated once when the whole GameReplicationInfo actor is initially replicated to the client, and then the client increases ElapsedTime and decreases RemainingTime once per second all by itself. Sounds simple enough, but somehow the initial value replication doesn't seem to work – and that's not just in Jailbreak, it's exactly the same in vanilla Deathmatch. Consequently, the time display in the scoreboard is out of sync with the actual game time.

The programmer who thought that up at Digital Extremes or Epic must have anticipated some of those problems though, because it actually gets a bit more complicated than what I described above. In addition to ElapsedTime and RemainingTime there's a variable called RemainingMinute that is replicated every time it is changed. The Timer event in DeathMatch sets it to the current value of RemainingTime every full minute. GameReplicationInfo in turn picks it up and copies the replicated value of RemainingMinute to the client's local RemainingTime, thus re-syncronizing RemainingTime with the game's actual time.

Since I'd like Jailbreak's spiffy game time clock in the scoreboard to show the right time at all times though I guess I'll have to look for a fix.

Update: Two distinct problems there: Firstly, the initially replicated value of RemainingMinute always resets RemainingTime to the last full minute on mid-game connect; secondly, there can be an almost arbitrary delay for precaching textures and other content between PostNetBeginPlay (when the values for ElapsedTime and RemainingTime have just been replicated) and the first call of Timer (where they're updated client-side).

May 7, 2003

...so in Jailbreak the client sends a replicated function call to the server which directly responds by sending another replicated function call back to the client along with the current server time. The client calculates a time offset between itself and the server; and if it also takes the time into account it took the response function to be called on the client after the synchronization request had been issued, it's possible to synchronize client and server time to a precision of approximately a tenth of a second.

That happens once a client has finished loading and precaching the game. GameReplicationInfo only replicates the server time the match started, and determines ElapsedTime and RemainingTime based on the current client time and its offset to the server time.

Comments

Kuhal: I am interested in the replication behaviour you have discovered here. It may lead to an explanation of some behaviour I am getting during a pause in a network game. The client's countdown clock rapidly counts down the seconds that have passed while the match was paused. While I am certain the remaining time on the server has indeed been paused, it seems that the client thinks it knows better and catches up. The weird thing is that the game ends "on time" when the client ramaining time reaches 00:00. Are there any more references to how these attributes work in UT?

May 22, 2003

"All you can eat" for 5 ? in the uni's cafeteria today. Burp.

Jailbreak's [scoreboard] displays a minimap showing the current locations of your teammates. In network games that means that those positions have to be replicated to all clients. That leads to jerky movement of the dots on the minimap because the locations are updated only every couple of frames, not every (client-side) frame.

That problem can be somewhat relieved by using the Pawns' actual positions when they happen to be present on the respective client; but the other player positions still jerk. Some sort of motion prediction or motion interpolation should be built in; and preferably in a way that does not multiply the bandwidth requirements of the whole separate location replication business – my bandwidth conscience is striking already without that. That rules out simply replicating the player's velocity along with their position.

Since the minimap is by design mini, the updates usually don't go beyond a movement of a couple of pixels each time; and it's more to show the players' rough current whereabouts anyway, not their precise locations. Maybe an algorithm that "learns" about the frequency of the position updates and interpolates between the last two replicated known points (and beyond if the next update isn't received in time) might yield smooth and reasonably accurate results.

Unrelated to that I also wonder whether I should keep up the splines connecting the player names to their location dots on the minimap. I'm starting to think that simply attaching a small tag with the player's name and possibly a health bar would be nicer.

May 24, 2003

The principle works, but the movement is still jerky. Motion prediction doesn't work well because the time that passes between a replicated variable being set on server-side and its new value being received client-side varies wildly: So even when the player is moving in a straight line at a constant velocity, the client-side predicted location has to be corrected with every update.

Now I'm pursuing a different idea – adding the received location updates to a client-side list and trying to have the client interpolate along that list of locations, choosing its velocity independently as to keep up with the succession of new locations that are added to the list.

When the client-side interpolated location information reaches the last known location in the list and the player is not known to have stopped moving server-side, it extrapolates the player's movement using the last known direction and velocity. That can even be used to save bandwidth: The server only sends another update when it expects the client-side extrapolation to have deviated from the player's actual location too much.

May 25, 2003

To my mild surprise what I've implemented now works much better than I had dared to hope.

The server sends occasional location updates to all clients; and in addition to that, the absolute value of the player's movement velocity. The direction of the player's movement is derived from the succession of location updates; as described in the previous entry it's impractical to derive the velocity as well from the location updates because time differences can't be relied upon when network replication is concerned.

Using the absolute value of the player's velocity instead of the velocity vector itself saves some bandwidth – two thirds in the size of the replicated data item alone (vector consists of three floats, its absolute value of only one), and it's also much less likely to change (the absolute value remains the same even when the player changes his movement direction or strafes).

Still, with every location update there's bound to be a small deviation between the client-side extrapolated location and the updated absolute location sent by the server. Client-side, that deviation is simply smoothed out on a short time scale – that way the client-side motion simulation never suffers of any jerking motions, only small variations in movement speed which are virtually impossible to notice.

What I haven't implemented yet is the aforementioned optimization of location updates when a player keeps moving on a straight line for a while, thus making the client-side linear extrapolation sufficiently accurate even for longer periods between updates. No actual player does that in a firefight, of course; but it's not all that unlikely to happen in general, especially given that without that optimization location updates would be sent with a frequency of about five times per second.

Update: Reducing the number of location updates that way works fine as well. When moving on a straight line several seconds may pass between two location updates while the client-side minimap location indicator still accurately shows the moving player's constantly changing location, smoothly picking up a later change in the player's movement direction.

May 26, 2003

Would it still be fun if there weren't always more problems awaiting a solution?

When reading my code again I realized that it wasn't at all robust against server-side updates that never reached a particular client. While the mechanism doesn't rely on the time between two received updates anymore, it still assumes that the client gets to see every update the server sends – and that may not necessarily be the case. If the client misses one update, the client's idea of the player's movement direction will be completely off, especially since the server already sends only the minimum amount of updates that can be used to derive the direction.

So I have to make sure that the engine itself takes care that all clients always have a consistent set of information – the engine must know that if a client misses one or several updates it is necessary to send the last two location updates to that client next time. The easiest way to achieve that is by having two replicated location variables that are alternatingly set to the player's most current location – the other one then contains the location one step earlier.

However, the problem isn't completely solved already by that. Since those two variables (a two-element array, actually) are set alternatingly server-side (so that on each step only one of the values changes and has to be replicated), the client has no way to know which of the two replicated locations is the most current one – and so it can only derive the axis on which the player is moving, but not whether the player is moving "forward" or "backward" on that axis. The client needs one bit of extra information to resolve that ambiguity.

It's not as trivial as it may sound to derive a consistent notion of "forward" or "backward" for any given non-zero vector. Simply trying to define "forward" and "backward" in respect to a certain fixed direction is ambiguous as soon as a vector is lying in the plane perpendicular to the chosen direction; so that won't work (or rather, it will, but only in almost all cases, which is unfortunately not enough – Murphy knows). So, the method I've chosen to define "forward" and "backward" takes the signum of the first vector component (X, Y, or Z, in that order) that is not zero – that's guaranteed to be non-zero for any non-zero vector, and the resultant value changes its own sign when the vector does.

To sum it up, the client now has two replicated location values without knowing which one is the most current one; so it calculates the vector difference between those two and applies the notion of "forward" or "backward" on it (described above), checks it against the separately replicated bit of information sent by the server and changes the vector's sign if necessary. And Bob's your uncle. (See what Terry Pratchett does to one's vocabulary?)

But... wouldn't it have been much easier and just as effective if I had simply replicated an extra bit of information telling the client which of the two locations is the most current one, instead of laboriously introducing weird notions of "forward" and "backward" vectors? – Easier it would be but not as effective. That bit would have to be changed every single time the server does an update even if the player only made a slight correction in his movement direction; the method I've chosen only changes that bit when the player's movement direction changes from "forward" to "backward" (in the special absolute sense described above), so it only occasionally requires a change instead of always.

Addendum: I actually have it implemented by replicating that extra bit of orientation information as the velocity's signum – in an attempt to save bandwidth when both velocity and orientation change at the same time (and it also wouldn't take any more bandwidth when the velocity alone changes). Upon writing about it here it suddenly occurred to me that this maybe wasn't all that smart after all – if only the orientation changes but the velocity doesn't, the engine still has to replicate an entire float value even though only a single bit changed. Would it be more efficient to go the straightforward way and use a separately replicated bool variable? I'll keep a close eye on which updates actually need to be sent by the server in a real-game situation and decide then...

(And that's also why writing in this "developer journal" is worth the effort even if nobody's actually reading it – it makes me critically review what I've coded. It's a one-person code audit, so to speak.)

May 30, 2003

So, at last, the scoreboard is feature-complete. Phew. I dread writing the log for the CVS commit and getting JDN up to date... but the sooner I start, the sooner I'll be finished.

Showing the Jailbreak scoreboard has a noticeable impact on framerate at the moment – I haven't been able to spot a certain, single part of the code that's responsible for the majority of the framerate hit. Disabling certain scoreboard elements one at the time shows that the clock takes as much performance as the dotted splines do, and the code that updates the scoreboard's state with the game's live information isn't far behind. The performance hit seems to be evenly spread over most the code.

Especially something like the clock could benefit from some sort of graphics cache – it changes only once per second, but has to be rendered every frame. If I could draw the clock on a ScriptedTexture and then on the screen, and update the ScriptedTexture only once per second, that might save several milliseconds per frame... given that I'd manage to get alpha transparency to work on a ScriptedTexture; I wouldn't like the clock to be displayed on a rectangular black background, not even for performance's sake.

Maybe there is a way to use alpha transparency on a ScriptedTexture. During my experiments around drawing a map screenshot on a ScriptedTexture for the scoreboard minimap I noticed that the ScriptedTexture's DrawPortal function did, somehow, alter the ScriptedTexture's alpha plane – it seems that wherever DrawPortal renders a normal solid texture, the ScriptedTexture will be completely transparent, but where an alpha-masked texture is drawn on the ScriptedTexture by DrawPortal the ScriptedTexture adopts its alpha mask there. It's all pretty strange.

I also remember that in Unreal Tournament a ScriptedTexture could have a "background texture" that was used to initialize the ScriptedTexture canvas every frame. I haven't found a corresponding property in UT2003's ScriptedTexture class, but maybe it's reusing an inherited property for that purpose – FallbackMaterial maybe? Gotta try that when I'm home.

May 31, 2003

I've finally decided that the manual implementation docs I had put on JDN have outlived their purpose – they originally were the detailed specifications for the code before UT2003 was released, but after the classes were actually created there's little point in still keeping the specs around and manually up-to-date.

I tried UnCodeX's HTML export, and while it works fine, it's information overflow. I think people interested in Jailbreak 2003 implementation details do better with syntax-highlighted source code alone – I tried to document it well after all.

The Wiki script running JDN now supports an extra parameter for the <uscript> tag that directly takes the sources from the CVS repository and renders them on the page. It looks pretty neat and saves me lots of time keeping the specs consistent with the code. Maybe I'll later add a DHTML feature for "collapsing" function bodys to keep only the comment headers visible by default.

Update: Oh, and FallbackMaterial as a ScriptedTexture's background texture does not work. Guess I'm out of luck.

June 4, 2003

I want to keep players from using their translocator to get out of jail the easy way, and I don't want them to be able to telefrag Bonus Pack monsters.

Unreal Tournament had a handy AllowTranslocation query function in DeathMatchPlus for that. Whenever anybody, player or bot, tried to translocate, this function was called and had the opportunity to prevent the translocation attempt.

Unfortunately, there's nothing like that left in UT2003. The only thing that remains is a function in SquadAI that's queried for whether a bot will try to translocate. The code that prevents players from telefragging teammates is buried in the Translocate function in class TransRecall? – it's basically a hard-coded check whether two players' collision cylinders impinge.

The only possible way into this code is via the SameTeamAs function (class Controller) used there that in turn calls IsOnTeam (class GameInfo). The game could pretend that monsters are on the same team as any player trying to telefrag them, but SameTeamAs is used by other parts of the game code as well, which makes (ab)using it to prevent monster telefrags not straightforward.

And it still leaves me with the question how to prevent players from translocating out of jail...

Update: I've ended up with removing the translocator from jailed players' inventories. It's not all that messy after all.

Comments

ZxAnPhOrIaN: Maybe make an actor or volume that kills the translocator beakon when you try to teleport out, killing the person.

ZedSquared (from a UT classic point of view, dunno if any of this is feasable in 2003) Have a spawnnotify that catches transloctargets and modifies their velocity to kick them back to the owner (or the middle of jail) if they're in the jail zone perhaps ... or maybe subclass translocator, give the modified one as default inv, and override the fire function to not work in jail ... probably duff ideas, just thinking aloud late at night :)

Mychaeel: I'd rather try not to replace the normal translocator to achieve the effect I'm looking for (plus, there's nothing like SpawnNotify in UT2003)... and I'd also like not to require mappers to put in special zones or volumes. (Right now jails work with any volume or zone a mapper places – attaching them to a JBInfoJail actor makes them part of a jail.) – I could simply take a player's translocator away when he enters jail and give it back when he leaves jail, but that seems a bit messy.

Foxpaw: I don't see the problem - can't you just disallow translocator use in jail like in Jailbreak for UT?

Wormbo: That's exactly the problem, there is no AllowTranslocation function like in UT which allows the gametype to prevent translocation or add various other effects like flagdrop etc.

Foxpaw: Well, in that case why not allow translocation in jail, and just have surfaces that make up the jail solid? That way the translocator beacon would not be able to be launched out of the prison so a player couldn't escape merely by translocating.

Mychaeel: That would be a mapper-based solution, but the past (Jailbreak III) has shown that even in jails that were meant to be airtight (and must have been in editor terms since there a jail was defined by a distinct zone) occasionally had collision "gaps" in them that allowed players to shoot or, possibly, throw a translocator target outside (JB-Raid). Even if a jail really is airtight it may be possible to place the translocator target so that translocating to it will place the player outside jail (JB-Antipody). Besides, some jails are meant to have open windows to the outside or escape routes that players should be able to walk, but not translocate through.

June 6, 2003

The Jailbreak core to-do list has become pretty short. Maybe, maybe, in a few weeks...

June 8, 2003

BeyondUnreal's forum breakdown will leave Jailbreak's development forums completely empty when they're back up. Most of the hundreds of postings in there I'd have liked to keep mainly to keep a history of Jailbreak 2003 development; but some of them were ongoing discussions and long-term documentation.

Unrelated to that, early this afternoon my personal computer at home broke down as well; that leaves the CVS server and Jailbreak Developer Network offline too. Fortunately the data itself wasn't lost; just my computer won't work. If I'm lucky it's only either the power supply or the mainboard. The bad thing is that tomorrow is a holiday here, so I'll need to stick with this notebook as a temporary measure till I have a chance to buy replacement parts.

Comments

Tarquin: I'll do my best to remember what's been going on. [Jailbreak rescue]?

Mychaeel: That's a great idea.

Tarquin: Maybe I shouldn't put code dev topics on a public wiki though. I can just scribble a text file, or on the JDN

Mychaeel: JDN is down along with my computer, I'm afraid. Tahngarth has set up a temporary Core Dev forum at http://www.planetjailbreak.com/tempforum/ – register there and meet Tahngarth or me in chat to get access.

June 9, 2003

After finishing some Perl coding work (which I'm actually getting paid for) I'll see that my computer is restored to working order or that I at least find out which component exactly needs to be replaced.

Update: The computer boots up with an old PCI video card; but as soon as I replace it by a AGP video card (either my current GeForce 4400 Ti or an older GeForce 2 MX), I only get either a strange creaking sound from the PC speaker or nothing at all. Only the fans work.

A friend of mine who's professionally dealing quite frequently with broken PCs suggests that it might be either the power supply that doesn't manage the AGP cards' load anymore, or the mainboard for some reason doesn't like AGP cards anymore. Unfortunately I don't have a spare ATX power supply at hand, so I won't be able to test that theory until tomorrow at the earliest.

In case it's the mainboard I'll at least use the opportunity to upgrade my old Athlon 800 MHz to whatever it is you can currently still get in a regular computer hardware shop – apparently it's hard to find anything slower than 2.0 GHz nowadays.

June 10, 2003

My computer is up and working again. I was surprised that for not more than about 180 ? I got a new processor (Athlon XP 2000+) along with a mainboard, silent cooler and memory (512 MB). I could even keep my previous 235 W power supply. By accident the processor is currently underclocked to 1.25 GHz (its normal clock rate would be 1.67 GHz) – I'm not even sure whether I can be bothered to open the case once more and set the jumpers correctly.

So far the system seems to be stable. Time will tell.

June 11, 2003

Bots should simply try to avoid being killed when "evasive" team tactics are selected. (Useful when a team has a narrow lead shortly before the time limit runs out.)

I got that to work reasonably well with relatively little effort, code-wise: Set the bots' aggressiveness to a really low value to ensure they don't feel inclined to engage in fights; then overwrite PickRetreatDestination and make it react in a suitably cowardly manner if the bot is seeing an enemy. Right now they simply pick the NavigationPoint among their current MoveTarget's ReachSpec?s that's farthest away from the enemy, or the closest one that provides cover. Effectively that makes bots run away from an approaching enemy and make turns in order to move out of the enemy's firing line.

While it's fun (for a while) to chase bots around in a test map, I've come across one or two situations where bots are effectively stuck and too easy a prey to approaching enemies. First I'll have to have bots detect this; then I'll have to have them react to it suitably, for instance by either simply choosing another way out or by acting like a cornered beast and falling back to normal dogfighting tactics until they're either dead or the threat is gone. (Tomorrow more – I'm tired.)

June 13, 2003

I found one reproducible situation in which bots are stuck in a tight loop of pathnodes they visit: A bot sees an enemy, turns to flee, runs into cover; arrived there it wonders what to do next and decides to go on an inventory hunt, which leads it back into the enemy's sight – ad inf.

The bad thing is that bots (or rather the native function FindBestInventoryPath in Controller) are not smart enough to check whether they even have the chance to pick up the inventory when they've reached it – otherwise they wouldn't even try to reach it more than once in a row, and my problem would be a non-issue.

June 14, 2003

A comment in Bot? hints that Epic meant to add a function called FindPathAwayFrom at some time – alas, they didn't yet. That leaves me with either sticking to my simple escaping algorithm from above (and its flaws) or creating a path finding function myself, in UnrealScript.

I've started giving the latter a try. The idea is to recursively search the path network for NavigationPoints that provide cover from the enemy, starting at the bot's current MoveTarget. Some first test runs seem to indicate that it works, and it seems to have negligible impact on the performance so far. (The Clock/UnClock mechanism described on Optimization Techniques proves pretty useful for such profiling.)

Update: If a bot doesn't find cover nearby and realizes that the enemy must have seen it (because it isn't looking the other way right now), the bot panics and reverts to normal attack mode until the enemy is dead or out of sight. I've put all that into CheckSquadObjectives now, and it seems to work pretty well testing it with a single masterful bot.

June 16, 2003

Hmm. Why on earth does the game rules tab maintain its settings in separate config variables instead of taking and storing them in the game type's own configuration?

June 17, 2003

Why does filling the mutator list box on the "Mutators" tab involve two other separate classes? Why is reading a list of available mutators a native function?

Comments

Wormbo: I guess it's supposed to speed up loading of mutator classes, but it's not really that much of a boost compared to the "old" GetNextInt(Desc) method because now the mutators' code packages have to be loaded in order to find the mutators' names, descriptions and groups.

Mychaeel: I wonder whether that native function (GetMutatorList in xUtil?) does indeed load the mutator name to get the mutator name and description – a comment says it does not fill in the MutClass element of the MutatorRecord structure, which would make little sense if the class is loaded anyway. (Unless GetMutatorList was internally "optimized" only to look up the class's default properties without loading the class; but I'd be a bit sceptical as to the wisdom of that optimization. But maybe it has to do with cheat protection validating all loaded code...? Gah.) – There's an UnrealScript function LoadClasses in xMutatorList? that would load the mutator classes, but it doesn't seem to be actually used.

Wormbo: It's not the only questionable optimization. (Still remember "// FIXME THIS SUCKS"?) GUIController.GetWeaponList also is a native function and it definately loads and returns weapon classes. I can't see any real optimization over Unreal's or UT's code here.

June 18, 2003

I'd like to intercept when somebody is clicking on a checkbox – not just the part with the check mark but its label.

It took a while until I figured out that the bAcceptsInput flag must be set to True for any of a GUIComponent's delegates to be called. (Mmph.)

June 23, 2003

The OnDraw delegate is called for custom GUIPanel? subclasses but not for direct subclasses of its parent class, GUIMultiComponent. Does that have a deeper sense or did just somebody forget to add a call to the delegate in GUIMultiComponent's native Draw implementation?

Update: Is it just me or does setting the TextFont property in GUILabel (and, thus, the LabelFont property in GUIMenuOption) have no effect at all? (Obsoleted by the introduction of GUIStyles? maybe?) All labels in the game seem to have the same font, even those where Epic's own UnrealScript code decrees otherwise by setting TextFont.

June 28, 2003

This one has me baffled (in a WebApplication subclass):

function QuerySomething(WebResponse WebResponse) {
  local string ResultTemplate;
  ResultTemplate = "should be overwritten by the next statement";
  ResultTemplate = WebResponse.LoadParsedUHTM("something.inc");
  }

Now I'd expect ResultTemplate to contain the result of parsing the file something.inc... right? And so it does, but appended to the previous value of the variable. The resultant value of ResultTemplate is something like this:

  should be overwritten by the next statementContents of something.inc

Is that an engine bug? Some arcane feature? How can using LoadParsedUHTM on the right side of an assignment change the way the assignment operator works (given that the return value of LoadParsedUHTM is a mere string)?

However, the following workaround solves the problem:

  ResultTemplate = "" $ WebResponse.LoadParsedUHTM("something.inc");

Trying to cast the right side to string in an attempt to do the same ironically yields a "No need to cast StrProperty to itself" compiler error.

July 3, 2003

Working on an HTML parser and formatter in UnrealScript again. This time it'll be standalone and not part of Screen, even though I intend to use it for that end as well; and this time it's possible to use it on both ScriptedTextures and a regular Canvas.

I hope I'll get around to implementing tables and floating images as well this time. The base architecture I'm currently designing and coding takes the requirements for those features into account already, so it should be straightfoward to even follow W3C's specification of [calculating the width of columns] according to the standard.

July 5, 2003

The native Split and Divide functions turn out to be extremely handy for the kind of text parsing I'm doing at the moment. (But I still want my native regular expression support in UnrealScript... mmph.)

Comments

El Muerte TDS: Nah, regular expression suck for text parsing, they are only useful for match (and replace). Using a tokenizer is much better for text parsing. It would be better if the native tokenizer was available in uscript.

Mychaeel: I dare say that depends on how you'd use regular expressions. The _nextToken function in your tokenizer could be formulated much more efficiently using regular expressions, for instance. (Though I agree that the availability of regular expressions might lead some people to creating terribly inefficient parsers.)

For that matter, my prime concern about this HTML parser is its performance, so parsing through the input stream character by character in UnrealScript is something I'm determined to avoid. (The first version of Screen's ScreenSlidePage HTML parser did that, and it was slow as hell for anything exceeding very few kilobytes of input text.)

July 6, 2003

The HTML parser works like a charm since yesterday and nicely dumps the structure of a parsed HTML file, though I haven't made any performance evaluations yet; but in any case there's even some potential for optimization still (like identifying a tag's handler class by something faster than the sequential search I've settled for until the rest works, like binary search – did I mention that native hash support would be a great addition to UnrealScript too?).

Comments

El Muerte TDS: There is a type called map, but it's only native atm (just like dynamic array used to be).

July 9, 2003

As usual it's not the normal case that makes implementing something a challenge; it's the pathological cases that do.

The obvious step after implementing an HTML parser is creating a formatter for the parsed HTML element tree. The old one I made for Screen (one of my first UnrealScript projects altogether) has no concept of tables or floating boxes, but this time I'm determined to add support for those and so I have to deal with the conceptual odds and ends that come with it.

A floating box is actually a pretty straightforward concept: It's a rectangle that's placed at the left or right edge of the containing box starting at the vertical position of the text line it's defined in. That technique is most commonly used to have text flow around an image on a website.

Normally, a web designer adds floating elements at the very start of a paragraph so the boxes are placed already when the formatter starts wrapping lines between them.

The beginning of a paragraph is, however, not the only valid place for floating boxes: They can just as well be defined in the middle of a line, and that's where the trouble starts. As long as the current line is short enough to fit next to the box, everything is still fine; just add the floating box and shorten the line box accordingly.

But what if the floating box is too wide to fit next to the text line where it's defined in? The HTML standards document says that the box is then to start directly below the line it is defined in; that's simple enough to implement... but subsequent text after the definition of the floating box is still to be added to the very same line above the box, and according to Murphy's law somebody is bound to have some item in that line after the floating box definition that increases its line height. That means, basically, that the absolute position of the floating box may not be completely determined at the time it is added. Consequently, it may be necessary to shift the box down after it has been rendered.

July 14, 2003

I just realized that I failed to enter UMake for the contest. Ah well – it sorely needs an update anyway given the huge list of pending fixes and improvements, and there's a lot of time till the deadline of phase two.

It's a bit of a pity that MeshMaker isn't eligible. It might have had a chance to win something.

Comments

Haral: FYI, the deadline for the contest is tomorrow. You can still enter, I do think.

Mychaeel: It is? Well – hardly enough time to give it the overhaul it deserves unless I was really desperate to enter it in phase one, but since there are two more phases to go, I think I'll just target phase two for now. But thanks for the correction. :-)

Haral: Those were my sentiments as well. There's a lot of work to do on my mod, playable as it might be, before I'll feel it's ready for any sort of release. Thanks for moving that comment btw, my brain doesn't exactly work well while I'm at work. Anyway, time to get back at it, if I want to be ready by October.

July 16, 2003

Trying to read a binary file in UnrealScript.

The LoadParsedUHTM function in a freshly created WebResponse object does well enough for text files, but I ran against a fundamental problem of UnrealScript's string data type here: Obviously it's not as high-level as it should be in a language like UnrealScript and doesn't handle embedded null bytes (Chr(0)) well. A null byte terminates a string for all UnrealScript-related intents and purposes; so the content of the binary file I read is clipped off after a few bytes when the first null byte appears.

The only solution for that I can currently think of is briefly opening a TCP link from the game to itself, serving the binary file using the IncludeBinaryFile function in WebResponse and receiving it with a simultaneously spawned TCPLink? in binary mode. But that's not only quite cumbersome, there's also a chance that some people's personal firewalls might complain about it (even though it's nothing but a local intra-process connection)...

Comments

Wormbo: AFAIK Quake 3 only works like this, even a single player game connects to a local game server, and when playing a skirmish game with open slots other players could join it, expanding it to a network game.

Nytro: I think this now constitutes more than "short thoughts that popped up during Jailbreak development." o_O

Mychaeel: You're right. I've scratched the "short" part in the description.

Nytrogen: I hate to continue to bother you... I don't own UT, and I can't find a decent description of Screen. Would you mind giving me a link or quick 'Screen overview' blurb?

Wormbo: You just linked to it. There are links to the Screen website and forum. You should find any information you need there.

Birelli: Specifically the "about" page: http://www.planetunreal.com/screen/about.html on the Screen website.

October 1, 2003

This morning I officially handed in three copies of the physics diploma thesis I had been working on for the last year (and which had kept me off anything Unreal-related for the last two months). For those interested, it's about Metal-Induced Crystallization of Silicon-Germanium Layers – fundamental research for the next ('third') generation of solar cells which will be far more efficient and much cheaper to produce than what's currently on the market.

Well. [takes a deep breath] This concludes more than half a decade of studies of mine at the Technische Universität München (I recently learned that they attach great importance to not being called "Technical University Munich" or something like that even on the cover sheet of English-language publications such as my thesis). I guess it'll take a couple of days until I have really, deeply grasped the significance of this. Right now, I'm mostly just relieved, happy, and tired.

One thing is sure, though: I'm ready to get back into the Unreal community. Even though I had been preoccupied by other things, I've really missed it. :-) It'll take me a few days to catch up, however, and there's also a lot more in my life which somewhat suffered during the last few weeks and which I have to catch up with, so be gentle.

Comments

Sobiwan: Solar cells: nice. Enough of this fossil fuel baloney. I hope your thesis helps push research and/or development further.

Tarquin: Yay! Welcome back Mych. Good work saving the planet :)

El Muerte TDS: heh, it's not like this is the only planet we have ;) About the uni name, ofcourse they want it to be the german names, the english name is too easy to write, it doesn't have ä and ü. They want you to put effort in typing the name.

Mychaeel: Well, I got a German keyboard here, so effort is not an issue. – The [Walter Schottky Institute] where I worked, on the other hand, does not have any problems with officially allowing the English name Walter Schottky Institute for Fundamental Research in Semiconductor Electronics. – But then again, its name doesn't have any äs and üs in it, so your argument still holds. ;-)

Tahngarth: Well I hope you get an A (or whatever the best grade is) :)

GRAF1K: Good, NOW GET TO WORK ON JAILBREAK!!! :P Glad you're back. :-)


Category Journal

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

Mapping Topics

Mapping Lessons

UnrealEd Interface

Questions&Answers

Scripting Topics

Scripting Lessons

Making Mods

Class Tree

Questions&Answers

Modeling Topics

Questions&Answers

Log In