| Home Page | Recent Changes

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 Bots 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 ReachSpecs 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 Code Optimization 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. :-)

March 1, 2004

* Mychaeel blows the dust from his developer journal, opens it and continues writing.

Just checked in some actual, new, original Jailbreak code again yesterday. Quite an elevating feeling.

It's a facility for our audio announcements. We noticed that the number of distinct announcements grew exponentially as we thought of more and more situations in which one would make sense. Some documentation of the system is available on [JDN: JBSpeechClient].

Comments

Tarquin: Wow! :) Looks cool!

April 1, 2004

Short of a few rather small features and known bugs, Jailbreak 2003 is finally feature-complete. Yay!

The transition from UT2003 to UT2004 has put a big item on our to-do list again, though. Epic has broken source code compatibility on several places, and other problems seem to stem from binary incompatibility (the Ping variable in PlayerReplicationInfo suddenly transmogrified from an int to a byte, which breaks the score display in our scoreboard).

It's strange, yet interesting and somewhat impressive, that UT2004 manages to load and run the game anyway. At the surface, Jailbreak 2003 runs well under UT2004 already, even though it looks like Jailbreak 2003, at least HUD-wise. Of course we're going to make Jailbreak 2004 blend in with UT2004 as well as Jailbreak 2003 does with UT2003.

If possible without too much pain I'd very much like to release Jailbreak 200x as a combined UT2003/UT2004 release. The changes are small enough that it would be a shame to abandon all UT2003 users who didn't have the chance or the money to upgrade to UT2004 yet.

I'm looking into the details of CVS branching right now. Our "main line of development" will become UT2004, but we'll keep a UT2003 branch and take care to merge any generic changes into it. ...if that works the way I hope it does.

April 23, 2004

The Jailbreak to-do list is empty, and I'm out of work. (You bet.) Guess it's beta time now. Yay! :-)

Currently working on a Perl script that automatically updates all required modules from CVS, checks for uncommitted changes, rebuilds them if necessary and automatically creates the distribution packages. I hope to be done with that sometime tonight. My girlfriend made us a spiffy installer logo – I'm looking forward to hearing what the others say about it...

Comments

Corran: That's great news (for us anyway :D). I can't wait for this.

GRAF1K: What he said. My mouth is watering. :D

BTW, did you know that UW is integated with the UT2004 UDE? Click on the Documentation tab in the code editing area and and there's unDox and Unreal Wiki. Sweet. :)

Corran: Yeah, it's been integrated into WotGreal fo a while now.

GRAF1K: Who cares about WOTgreal... UDE is official. :D

DJPaul: WOTgreal is the UDE.

Guest: Like the core of the application changes anything whatsoever... O_o

Wormbo: c-24-7-171-253.client.comcast.net (Guest above), will you please stop deleting Murphy from this page?

May 17, 2004

Let it never be said again that modifying the mid-game menu in UT2003 requires a custom PlayerController subclass...

[mychaeel-ut2003midgamemenu]

May 19, 2004

I'm practically out of ideas what else could be added to Jailbreak – almost time to release now...

Last thing we implemented were custom loading screens – it's actually pretty straightforward to replace the default images displayed while a map loads by custom ones at runtime.

We've even managed to create one loading screen per bundled map and have the corresponding screen shown when the map loads – and I've added a means for mappers to [embed a custom loading screens into their map] which is displayed when that map loads. Given that the necessary information is all there and is just being ignored by UT2004 stock code, I wonder why Epic didn't do that themselves...

Comments

GRAF1K: Epic wanted to include their uber-1337 Photoshop lensflares though! ;)

Looks like a very nice release coming up from you guys over at Jailbreak. I'd count off the minutes if I only knew when...

Note: I am indeed the M*rphy killer. Buggy 3rd-party software... :pissed: I'll get it fixed. For now though, I can't even replace the M*rphy's once I've killed them. Just revert once you see this.

Mychaeel: Argh... what kind of bug would specifically replace occurrences of the string "Murphy" in a text...? o_O

GRAF1K: Well, it's not the bug directly that's doing that. The bug is me not being able to turn the feature off. The feature in question is a privacy feature that blocks sending of personal info (like the owner's name).

Mychaeel: So is it safe to deduce that your name is Murphy? (Way to go for a privacy feature... :-p)

GRAF1K: LOL. I was going to disable that feature anyhow. The app does a lot more than that. Besides, my last name isn't Mürphy, but the account is under someone who's is. :)

May 20, 2004

Naturally, any display of hubris such as making the statement "I'm out of bugs" angers the Gods, so the to-do list has a few more now. Fortunately, they're all pretty small yet, and I'm out to squish them before they get any bigger... ;-)

May 23, 2004

You have no idea how glad I am that we're making a UT2003/UT2004 release of Jailbreak – if only for the reason that on both my desktop computer and my notebook with their measly 512 MB of RAM starting UT2004 (or even UT2004 server and client at the same time) takes ages while the system swaps like crazy – and loading UT2003, even server and client, works without any swapping at all. UT2004 is a monster...

Comments

El Muerte: I don't have any swap issues when running UT2004 on my laptop (512MB ram). Haven't done much client+server testing, but the client takes quite some time to join the server (/me blames slow laptop HD). On my desktop I can run a server and two clients without any serious problems (1GB ram). I think windows is to blame :P

May 27, 2004

Jailbreak is out. We assume full responsibility for all bugs that were left in. :-p

Seriously though – everything went reasonably smoothly, and while there are some bugs we'll have to fix, nothing really serious has popped up yet. Feedback is very positive; feels good. :-)

I've submitted Jailbreak 2004 for the MSU contest. Perhaps we'll win something.

May 29, 2004

There's a known issue in UT2004 which prevents anybody who ever selected a third-party gametype in the server browser from joining any other game of a different gametype.

This happens since, when a gametype is selected, its GameInfo class is loaded and its static FillPlayInfo function is called. After adding its settings to the PlayInfo object, the GameInfo class remains loaded in memory (probably because it's somehow still referenced by the PlayInfo object and thus cannot be garbage-collected), and when the player tries joining a server running a different gametype, that'll fail because the third-party gametype's package is still loaded.

I haven't been able yet to find out why exactly FillPlayInfo is called at this point by the server browser and which part of the code does anything with the data saved in the PlayInfo object referenced by the FilterInfo variable. However, simply avoiding to actually add anything to the PlayInfo object seems to solve the problem without any noticable side effect, so the following chunk of code makes everything work as it should:

static function FillPlayInfo(PlayInfo PlayInfo)
{
  local UT2K4ServerBrowser UT2K4ServerBrowser;

  // check whether we are called from a server browser
  foreach Default.Class.AllObjects(Class'UT2K4ServerBrowser', UT2K4ServerBrowser)
    if (UT2K4ServerBrowser.FilterInfo == PlayInfo)
      return;  // ignore because called from server browser

  // now do the usual stuff
  Super.FillPlayInfo(PlayInfo);

  PlayInfo.AddSetting(Default.GameGroup, ...);
  PlayInfo.AddSetting(Default.GameGroup, ...);
}

May 31, 2004

While fixing some netcode I found out that in one of my classes a certain variable was constantly, over and over again, replicated to all clients – several times per second; that class had bNetNotify activated, and PostNetReceive was called every other tick.

What baffled me was that this variable wasn't even changed server-side; nor did it change on client side.

It took me a while to find out what was happening: This variable which was replicated over and over again was a Pawn reference. As soon as the Pawn it pointed to was destroyed, the variable was duly set to None on both server and client – but the server kept replicating its value to the clients. And at the end of the day, the follwing slightly weird piece of code fixed it all:

// fixes replicating over and over when referenced actor has been destroyed
if (PawnObjectiveGuessed == None)
  PawnObjectiveGuessed = None;

The Unreal engine is full of mysteries...

Comments

Mysterial: That works because UnrealScript returns None when accessing a property pointing to an Actor with bDeleteMe == true. Internally, the Actor is in fact still referenced by that property until the Actor is truly deleted. It getting continually replicated might be a bug in the engine. Can you try to create a small reproduction case and send it to me?

Mychaeel: Will do. :-)

Mychaeel: Hrmph... making a simple test case is not as straightforward as I thought. Then again, if it was an obvious bug, it probably wouldn't be in there... ;-)

May 31, 2004

Argh! People report server crashes, and every single crash log has the following lines at its end:

  Critical: UObject::ProcessEvent
  Critical: (JBCameraArena JB-Heights.JBCameraArena, Function Jailbreak.JBCameraArena.Tick)
  Critical: AActor::Tick
  Critical: TickAllActors
  Critical: ULevel::Tick
  Critical: (NetMode=1)
  Critical: TickLevel
  Critical: UGameEngine::Tick
  Critical: Level Abandoned Heights
  Critical: UpdateWorld
  Critical: UServerCommandlet::Main
  Exit: Executing UObject::StaticShutdownAfterError
  Exit: Exiting.

That Tick event in question doesn't do anything special that I'd be aware of – it certainly doesn't destroy the actor or anything like that. And I cannot reproduce it at all in a local client/server testing environment, and it only happens infrequently on other servers anyway.

There may be one thing: The Tick event calls a function within an editinline object (which was created along with the JBCamera actor as a subobject). Perhaps, when JBCameraArena.Destroy() is called, that subobject reference somehow becomes invalid and crash-prone under certain circumstances?

Update: I just found out that all my JBCameraArena actors share the same subobject – unlike what I expected, seeing that spawning Emitter subclasses creates distinct instances of their ParticleEmitter subobjects. While I'm not sure whether that's responsible for the crashes, it's definitely not what I had in mind and it's a bug when more than one JBCameraArena is active at the same time.

To make it worse, those error messages don't really indicate what kind of error happened (the lines preceding them are different and nothing out of the usual in every case – such as players joining or leaving; unfortunately I have no indication whether there's a temporal relationship between the events logged before the error and the error, but I believe there's not).

Is there anyone out there with Unreal-engine insight who might be able to tell me which kinds of errors would trigger those server log messages?

June 2, 2004

Phew. The Jailbreak server crashes are fixed (and stress-tested successfully on MyUnreal's ever-full server), and so are the "web admin does not come up again after level change" and "server does not react to UDP requests after a while" bugs. Those were the toughest ones to crack, mostly because I was fixing in the dark; the others were, while partly annoying, peanuts.

In retrospect it seems that the latter two bugs stemmed from the engine not fully cleaning up the previous level; and that, in turn, was apparently due to my storing some actor references in the class default values of a class – I did that only to compare the stored references to given ones for caching a function's return value over subsequent calls. Even if the actors were destroyed between two calls, I'd expect a comparison simply to fail.

After I worked around that problem (by storing the actors' locations in the cache – in this case that worked just as well), suddenly those both bugs (web server not coming up again, UdpGameSpyQuery failing to bind its port after a while) were gone all by themselves. So, note for the future: Never store actor references in default properties!

Comments

Foxpaw: That's interesting. I've stored actor references in default properties lots. (For instance, any actor can get a reference to the level's backdrop in my case by accessing class'Base.Backdrop'.default.Reference. Perhaps this behaviour is limited to certain classes?

Mychaeel: Perhaps, but I don't think so. The effect of doing that is hardly noticable in local testing conditions, in any case (otherwise I'd have seen it a lot earlier) – as I wrote, the most noticable effect was that the web admin WebServer didn't get cleaned up on level change and that – after many level changes – a server just didn't respond to UDP requests anymore. Otherwise, everything worked great.

Mysterial: Since classes are non-Actor objects, the usual issues with storing Actor references in them apply. I'm somewhat surprised it didn't crash. Perhaps because the engine doesn't actually destroy the class objects as it would most other objects you'd store references in, since you're playing another Jailbreak game so it wouldn't ever unload the resources.

Mychaeel: Frankly, I wasn't ever aware of "the usual issues" with storing Actor references in non-Actor objects – Jailbreak code still does that on several occasions (LevelInfo and TeamInfo references in the non-Actor objects which represent the small player icons on the HUD, and in at least one other place too). Are you saying that referencing an Actor from a non-Actor object is always crash-prone and not supported by the engine?

Mysterial: Actor references in non-Actor subclasses don't get automatically cleaned up by the engine, so they work just like C++ pointers in that accessing them after they've been deleted has horrible, unpredictable effects (generally crashing). Additionally, since part of garbage collection is traversing every object reference property that exists, you need to clean up any such references before a level change or you'll either get frequent serialization crashes or prevent the previous level from unloading, creating huge memory leaks, as you unfortunately found out.

June 8, 2004

With the stress of releasing Jailbreak over, I'm free to look into some of my old mutator ideas again. One of them is SprayPaint – a simple idea actually, borrowed from Half Life where the ability to spray-paint your clan logo on walls was built-in functionality. I want to port that idea to UT2004.

This would be pretty straight-forward and quite uninteresting if I hadn't put it into my mind that it should be possible to customize the spray-painted images by simply dropping a BMP file (1-bit, 64x64 pixels) into a directory; and, if that's done by a client in a network game, all other clients should be able to see the same image too. The latter is just some replication acrobatics; the former is where I saw my challenge.

Reading arbitrary files in UnrealScript isn't really supported, but if you need to do it for text files, instantiating a WebResponse object and using its LoadParsedUHTM function works quite well. However, UnrealScript strings aren't binary safe – null bytes act as end-of-string tokens and thus cannot be included in a string. Attempting to load a binary file such as a BMP image with LoadParsedUHTM results in a byte string truncated at the first null byte.

I've worked around that problem now by spawning a local WebServer listening on a random free port, delivering files as "application/octet-stream" with WebResponse's function IncludeBinaryFile, and a TcpLink operating in MODE_Binary to receive the binary data from the local web server. In theory this setup is able to read the contents of any binary file within the UT2004 directory hierarchy into an UnrealScript byte array, and it's working great for my purposes.

Comments

EntropicLqd: Can you see outside of the UT2004 directory structure with that technique? Cunning solution btw

Mychaeel: Frighteningly enough, I can read any file that way. (I had actually – and perhaps naively – assumed that the IncludePath in WebResponse would not allow UnrealScript code to travel beyond the UT2004 base directory...) – Perhaps it'd be best to let Epic know about this and to remove this last "Developer Journal" entry for the time being...

EntropicLqd: I'd definitely let Epic know about it (I guess you have already). Allowing access above the 2K4 root directory is an astonishing lapse on Epics part. I was expecting a ho ho ho ofcourse not response. Wonder if you could write a remote file browser...

Mychaeel: Without having a way to generically get full directory listings, I'm afraid (or rather, glad) that's not possible – StreamInterface? only provides a way to list directories (and a limited set of other file types, though I haven't been able to get that to work for anything except said directories and .mp3 files).

Mychaeel: The LoadParsedUHTM method works on any file on a given partition too, given the right IncludePath setting (e.g. "C:/") in a subclass and without all the local web server hassle.

Mychaeel: I've sent a mail to Joe Wilcox.

July 12, 2004

Jailbreak 2004 is one of the gametype finalists in the MSU contest. That's nice and I hope we'll win something too, but I find it more notable that Jailbreak got a honorable mention in the "FPS Mod" category. I didn't check that box when I entered Jailbreak 2004 into the contest; it must have been the judges' decision to put it there too...

In completely unrelated news, I've been turning some of my spare attention to attempting to write a couple of extensions for Thunderbird to make my life easier (on the long run; you know, that "impatience, hubris" part that occasionally pops up in a Larry Wall quote at the bottom of an Unreal Wiki page). Phew. If you ever thought that UnrealScript was a humongous, complex and for the most part poorly documented heap of code already, do look at http://lxr.mozilla.org for an upgrade.

Comments

Tarquin: I don't know about you, but I am finding the Jailbreak community very quiet these days. I'm sure there was way more activity (posts, maps, discussion) in the days of JBIII. But I suppose that was the 3rd release and we are only on the first. BTW, if you have some spare time, do you think you could look at the Wookee class tree generator? It would be nice if the text of the tree was parsed for wiki links, so we could use it for Unreal classes here – but I'm afraid I've not been able to work out how to do it. It's WookeeTree.pm in your CVS. :)

July 14, 2004

My support for Opera 6 has finally broken down after the last vBulletin 3 update which made [code] tags unreadable, and I've made the switch to Firefox. I'm loving it already.

It has gone quite a long way since the last time I gave it a try (must have been around version 0.6), and most of the reasons I didn't want to switch to it then have gone meanwhile. I'm especially fond of the ability to selectively skin websites with custom CSS (using the "uriid" extension and a specially crafted userContent.css), and I just have to love a program which allows me to tweak its user interface appearance by editing a CSS file...

In completely unrelated news, Fraunhofer IGD (whom I had sent an unsolicited application about three months ago and never heard back of) has offered me a job... working on a project in Seoul, South Korea. Imagine my surprise. I might even have considered it if I hadn't found another job in the meantime already and if I didn't specifically want to move closer to my girlfriend in the Frankfurt area. Still, quite interesting...

(Is this developer journal turning into a blog...?)

Comments

Tarquin: Bwahahahaha! I knew we'd convert you in the end.... :P

MythOpus: Consider this a reminder from the entire wiki to update the Wookee download zip ;)

August 20, 2004

When you think you're finally out of bugs, Atari QA will come and send you a new bunch of them. :-p

No, actually, it's not that bad. It boils down to one actual code bug and a bunch of map-related things in the order of "graphic seams flicker in and out around the pipelines." Still, I was surprised when I got that mail...

Comments

GRAF1K: Why would Atari be interested in sending you bug reports?

Wormbo: One code bug and one code thing I wouldn't call a bug. (no weapon in celebration screen)
They most likely sent the reports because they tested Jailbreak 2004. ;)

Mychaeel: ...and they tested Jailbreak 2004 because they want to include it in the Editor's Choice Edition. Sorry, I kinda implied this. ;-)

Guest: I figured ECE was the reason but wasn't sure. Congrats then. :D

September 5, 2004

Picking up my old SprayPaint mutator idea again. To reiterate, it's about being able to spray logos on walls – the logos must be provided as monochrome bitmaps (i.e. bitmaps with 1-bit color depth) in a certain UT2004 subdirectory, and you can spray them on walls in a color of your choice; and the best thing is that other players on the server would see the exact same thing even though they don't have the source bitmap on their system. On the long run I even want the sprayed images to persist across matches.

There's a bit of replication acrobatics involved in making that work, but so far it works nicely – the data is loaded client-side, transmitted to the server via a replicated function in "bunches" of no more than 64 bytes a tick, and then left to the engine to be replicated back to all (non-bNetOwner) clients.

Two things I noticed doing that were that, firstly, the engine only replicates the first 256 bytes of an array of 512 byte values (where I don't know exactly whether it's the number of elements or their total size which is limited by the engine); and secondly, that passing a byte array via a replicated function call will only actually replicate the first value of that array, not the entire array. However, if the array is part of a struct which is passed through the replicated function, everything works fine (hence my "bunches" of data).

The user interface for that thing is keeping me thinking. Right now I figure that pressing your usual "talk" key ("T", for instance) quickly twice in a row (like a double-click) would pop up a window where you could select which image to spray (using the mouse or arrow/movement keys), but that idea is subject to change. For now, the "mutate SprayImage <filename> <color>" console command does the trick.

November 17, 2004

I've moved into my new apartment last Sunday, and today I got ISDN and DSL activated. Thus, JDN is up again – whatever caused my computer to break down two weeks ago is mysterious to me. Maybe my video card overheated after all after having been running non-stop for months without a monitor attached to it... still, not really a satisfactory explanation.

Now that my home computer has been hosting CVS, an Apache webserver with JDN and other services and an ISDN calling machine and fax server for quite a while and across several hardware updates, I've been thinking – it's getting harder and harder to keep the noise level coming from this machine at a level I'm willing to bear in my living room. Plus, most of the time the computer is merely converting electric power into noise and heat via a video card and processor which are ridiculously overpowered for something acting as a web/fax/CVS server most of the time.

...so – *drumroll* – I've got that fabulous idea of building a small machine out of spare parts mostly left over from hardware upgrades of my main PC over the years. I've got two mainboards/processors to choose from (a Pentium 200 MHz and an Athlon 800 MHz which I could underclock to 500 MHz), some left-over RAM, an ethernet card (my latest mainboard had that on-board) and a simple video card (an ATI XPert@Work sporting a 3D chipset with the staggering amount of 8 MB VRAM). I also have a midi-tower computer case somewhere. What I need is a silent power supply and – probably – a new harddisk (especially if this thing is to act as a file server as well) unless I find a suitable one in my pile of used hardware.

I figure I'll install Debian Linux on it – with that awesome 3 MBit/s downstream I've got I can even download it without much hassle.

Update: Wow. Silent 80 GB harddisks come for less than 60 EUR these days? o_O

March 8, 2005

*blows dust off this page*

*cough*
*cough*

Remember the Palpable mutator for Unreal Tournament? Probably not, because I never actually released it, and the BuF mess-up two years ago ate all remaining evidence for it.

Basically, it allowed the user to see stereo 3D by displaying two versions of the current scene rendered from slightly different points of view. You had to squint to see the effect, just like for [these photos].

I tried to implement the same thing for UT2004 on Sunday. To the degree that two viewports were rendered on the screen next to each other, it was a piece of cake; but, having the HUD render within one of those viewports prove to be impossible. In Unreal Tournament, it was basically just a matter of setting Canvas.OrgX/OrgY and ClipX/ClipY right – but UT2004's HUD makes heavy use of the native DrawSpriteWidget function which, while honoring OrgX/OrgY and ClipX/ClipY, doesn't take the reduced size of a viewport defined through those properties into account when positioning and scaling the widgets.

So, no way. Well, it was just an experiment anyway. The 3D effect itself is pretty cool, though, even if it strains the eyes to squint that way for an extended period of time...

In other news, after a period of consideration, I bought a digital piano today – a [Roland HP-101]. I played the piano for years before leaving my parents' home to go to university, and I want to pick this hobby up again. And, wow, those digital pianos nowadays really do sound and feel like the real thing – down to subtle details like the slight resonance you hear after releasing a key, or that the keys for the deep tones react with slightly more resistance to being pressed down than the high-pitched keys...

Comments

Birelli: I remember Palpable, I think a couple of us on IRC tested it for you one time. It's definitely a cool effect, but it gave me a headache after about 15 minutes of playing. Probably not something people want to use all the time. Also it takes a bit of practice before you can train your mind into not forcing one eye dominant over the other, so at first it's basically impossible to run in a straight line.

April 19, 2005

Those who have followed [PlanetJailbreak news] are aware of what I've been working on recently: the visuals of freezing and shattering a player model. Part of the problem is to overlay the player model and the weapon it holds with an "ice" texture that gradually fades in.

My first idea was creating a Shader, setting its Diffuse material to the player's skin texture, the Specular material to my alpha-blended "ice" texture and use a ConstantColor as its SpecularityMask material to control the fading. That works fine for the player model, but utterly fails for the weapon – the weapon's textures are implicitly defined through the mesh, and there's no way UnrealScript code can get hold of them (not even anything similar to this method).

So, that approach fell somewhat short of what I had envisioned.

But there's OverlayMaterial defined in Actor, along with a SetOverlayMaterial method, which is used (among others) by the UDamage to produce that "energy" overlay effect on players and weapons. I had previously found out already that using a plain texture as OverlayMaterial will simply replace the model's skin by that texture (instead of overlaying it) – not what I intended, so I had put that approach aside. Now, I picked it up again.

After some lucky experimentation, I found that I was able to use a Shader as an OverlayMaterial whose Specular material was set to my "ice" texture. Funnily, I noted that:

  • The Diffuse material I tried to set was automatically overwritten with a reference to the overlaid actor's skin (the skin part with the highest ordinal number in the meshmap, I guess). So, here's a way to find the original skins of a non-static mesh if you need to.
  • The A (alpha) member of the Color property of a ConstantColor material I tried to use as the shader's SpecularityMask was immediately reset to zero whenever I attempted to set it to something different. Why would the engine do such a thing...?

So, that didn't work the way I had it intended to. Neither did any attempts to replace the plain "ice" texture in the Specular slot by a Modifier of any kind.

Then, in a last futile attempt, I tried using a FadeColor in the SpecularityMask slot (instead of a ConstantColor as before), and lo!, when I set its FadePeriod to something other than the default, zero, it worked! Well, of course I had a pulsing overlay now, but setting both Color1 and Color2 to the same color fixed that.

So, now I've got a texture overlay on both player and weapon which I can smoothly fade in (and out, if there was any need) and which still lets the original textures shine through.

Comments

Wormbo: Interesting hack. :) The engine uses the ConstantColor in the SpecularityMask slot to fade out the overlay effect when the actor's overlay timer drops below 1 or so.

May 1, 2005

Microsoft has a pretty good reputation on IDEs, but Microsoft eMbedded Visual C++ really has at least one major quirk which makes working with it a pain unless you either know how to avoid it or unwittingly got around it by failing to customize your workspace too much. I didn't fall into the second group, and it took me until today to become part of the first.

I'm developing a small smartphone application to remote-control MP3 playback on the Linux server which sits quietly in my closet via Bluetooth.

The Smartphone 2003 SDK comes with a smartphone emulator. That's very nice, but it becomes pretty frustrating once it stops working – which, for me, happened basically whenever I started the IDE for the second time after installing it: Whenever I wanted to run my program, I got an error dialog telling me one of the following things:

Cannot find a default device. Please create one or more from the Platform Manager.

There is no device installed. Please go to Configure Platform Manager to add a device.

Obviously there were still the same devices as the last time, and I certainly hadn't changed configurations since last successfully running the program in the emulator. So, almost each time I wanted to work on my project, I had to uninstall the entire IDE and reinstall all of it – two installers, one of them badly made, and a service pack; half an hour work just to get the IDE to work again. Needless to say, I didn't do that very often.

Of course, I googled for a solution. Nothing to find except for two postings from people with the same problem without any replies to them. At least I wasn't alone; little consolation there, though.

Today, I got that second error message from above for the first time, and that was my luck. Googling for that finally led me to a newsgroup message with the solution: If you disable the WCE Configuration toolbar, the (unlabeled) "default device" drop-down box (the rightmost one) will automatically revert to "Smartphone 2003 Device." Set it back to "Smartphone 2003 Emulator" and everything works again. There's no menu command for that option.

"Son of a gun!", to say it in the original newsgroup poster's words. What an unnecessary annoyance.

November 3, 2005

Today at around 11:15 AM, I successfully completed my twenty-ninth year of life (for a suitable definition of "successfully" :-P), heading into the thirtieth without hesitation. Happy birthday to me ;-)

Comments

El Muerte: damn you're old ;)

EntropicLqd: Happy birthday youngster (for a given value of young). Give it another few years and you'll feel like you are falling apart :)

Sweavo: phew it's not just me then Entropic :-)

Tarquin: Happy birthday Mych! The big 30 next year ;) It's not as awful as it sounds :)

Mychaeel: Hehe, thanks y'all :-)

MythOpus: I saw it awhile ago, but I was always on my dads Linux computer so I haven't been able to... WISH YOU A HAPPY BIRTHDAY!

Mychaeel: I'll take good wishes at any time, so: thank you as well :-D

Sweavo: in that case, have had a happy birthday :-)

April 24, 2006

Setting a data structure element of a replicated array element won't actually make the engine replicate that array element. You'll have to set the whole array element to make the engine notice that it has changed:

function TouchElement(int iElement)
{
  local TElement ElementTemp;

  ElementTemp = Elements[iElement];
  Elements[iElement] = ElementTemp;  // prod!
}

Has anybody else ever had that problem?

Comments

EricBlade: I haven't personally had a reason to make an array of structs yet, but it sounds like it could be related somehow to the problem of not being able to replicate -just- a struct, without some other var?

Wormbo: That seems to be a general problem of struct replication, not only structs as array elements. The code Elements[iElement] = Elements[iElement] seems to be enough to cause replication though, whether anything actually changed or not.

Mychaeel: Yes, I also realized that the detour over a temporary local variable probably wasn't necessary at all – I was still under the influence of the SwapElements function which actually needed one. I've occasionally replicated (individual instances of) structs without problems, though – you only have to be aware that changing any struct element will cause the entire struct to be replicated. (Actually, I've used that as a feature occasionally.)

July 15, 2006

I found the following function in [some source code] I had written years ago:

// ============================================================================
// CalcOrientation
//
// Calculates and returns an artificial value representing the orientation of
// a vector. The returned value has the following properties:
//
//   * Non-zero for any non-zero vector. Zero for the zero vector.
//   * Negating the argument yields the negative result.
//
// Those properties allow to use this function to make any difference between
// two given points in space unambiguous by passing a single additional binary
// bit of information.
// ============================================================================

simulated function float CalcOrientation(vector VectorInput)
{
  local vector VectorTranslated;

  VectorTranslated.X = VectorInput dot vect(1, 1, 0);  if (VectorTranslated.X != 0.0) return VectorTranslated.X;
  VectorTranslated.Y = VectorInput dot vect(1,-1, 0);  if (VectorTranslated.Y != 0.0) return VectorTranslated.Y;
  VectorTranslated.Z = VectorInput dot vect(0, 0, 1);  if (VectorTranslated.Z != 0.0) return VectorTranslated.Z;

  return 0.0;
}

It's part of Jailbreak's player movement extrapolation code which is designed to provide a smooth client-side representation of a player's position with as little network traffic as possible. The extrapolated player position is used by the [scoreboard minimap] and [JBScreen].

Well, what the heck was I thinking?

Obviously, instead of using a local vector variable, I could have used a simple float; the function calculates each component of the local vector, immediately uses it, and then never uses it again. Plus, looking more closely at what's actually done there, it looks as if I could simply have used the original vector's components instead of (sloppily) translating it into a rotated coordinate system first.

It took me a while to get behind what I had thought several years ago. I should comment more.

Using a local vector instead of a float was halfway between being an artifact of the way my mind had gone before arriving at this code and being self-documenting code. (Sure enough the latter could have been achieved just as well with less bytecode overhead by just adding a comment.)

Translating the input vector into a coordinate system rotated 45° away from the main axes was done to make changes in the signum of the return value less common. If there's any directional bias in a player's movement at all, it's usually along the main axes, following a hallway a mapper will generally have aligned along the coordinate system. Strafing left and right even just a bit while moving along one of the coordinate axes (say, the Y axis), would change the signum of one of the other coordinate components (the X component) frequently.

If the axes are rotated 45° about the vertical (Z) axis, chances are better that you stay "left" or "right" of an axis while moving, leaving the signum of the return value of this function unchanged. Any change in the signum needs to be replicated to all clients (as the signum of the scalar player velocity they use to extrapolate the player's position); so this coordinate rotation reduces the frequency of updates required to send to the clients.

Phew.

I remember experimenting with different replication and extrapolation mechanisms, using some of UT2003's more obscure logging parameters to get network traffic detail into UT2003.log. This optimization actually did reduce the amount of updates sent across the network for this detail. I never got around to actually measuring whether it made any relevant difference in the total amount of network traffic, though. I suppose this could be just one of the cases where I indulged in premature optimization just for the fun of it.


Category Journal

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

FAQs

Help Desk

Mapping Topics

Mapping Lessons

UnrealEd Interface

UnrealScript Topics

UnrealScript Lessons

Making Mods

Class Tree

Modeling Topics

Chongqing Page

Log In