Thread history
Time | User | Activity | Comment |
---|---|---|---|
No results |
I wouldn't mind doing that if the client would do a delay for 10 seconds or something before trying again after an error. Right now it just retries the battles that failed each iteration (along with the new ones) and this quickly leads to all clients just trying to upload at full speed the whole time, which will put too much load on the server.
If we wanted to change the client protocol so that the client had a delay when this happened, I wouldn't have a problem. However, another issue is that the priority battles get delayed then, which means that a) the bots that need priority battles end up getting too many once the queue is run and b) new bots take a long time to enter the rumble. So, optimal client behaviour would be if the server returns OK. QUEUE FULL. then the client should wait 10 - 30 seconds then retry uploading the same pairing to the server.
PS. the new rumble structure lends itself well to bulk upload strategies. If you want to write a bulk upload protocol I would be happy to look it over and implement it on the server.
I´m writing a custom client right now (slowly writing it from scratch in the last months). Yesterday I managed to make it fully functional, although it still needs polishing (make hardcoded behaviour more configurable). I´ll make it available here after it becomes more stable.
I can add bulk upload, but it will break compatibility with the current protocol. Unless both clients and servers support 2 protocols at the same time.
Features I managed to include in this custom client so far:
- Full compatibility with the current protocol. (I hope that underscore bug was the last one)
- Multiprocess/multithread support.
- Parallel downloading of JARs. (currently hardcoded at 15 simultaneous downloads)
- Processing battles in parallel while uploading results in a separate process. (currently hardcoded at 1 simultaneous upload)
- Abusing the Java 5 concurrent API to keep the code readable in the presence of parallelism.
- Upload throttling in case of errors (currently hardcoded at 10 seconds delay after each error). It is possible to throttle uploads in the absence of errors too, although I wasn´t planning to do that.
- Smarter handling of priority battles. One big pairing matrix handles priority battles, new competitors and competitors with low battle count, all at the same time. And it is independent from iterations (which I eliminated).
- Communication between processes through the network, allowing clients spread accross a LAN. (currently hardcoded at "localhost" address and 1099 port only)
- Automatic copying of JARs between clients. If a single "server" process has all JARs, no client needs to download from internet.
- Logging support. No more System.out.println. You can configure how messages appear in the console (or in a file), adding for example, time and severity.
- Internally, battle parameters are all dynamic. Parameters like number of competitors, inactivity time, gun heat cooldown, codesize classes, hideRobotNames, are all concentrated in a configuration class. The idea is to put them all in configuration files and make divisions like twin duel, team melee or anything else fully supported.
Neat! Sounds like a lot of work. What's the setup like for multi-process battles? And is it the same mechanism locally vs clients across a network?
There is a "server" process and multiple "worker" processes. You start the server process by calling server.cmd. And start each worker process by calling worker.cmd. Each one runs in a separate JVM and needs its own Robocode installation. This way each process runs in a separate window and you can see what each is doing.
All communication to LiteRumble is done by the server process alone. Server and workers communicate through RMI.
Server process is currently using the same configuration file of the official client. Worker processes are currently 100% hardcoded, but server address/port and robocode home could be configured.
Server process downloads participants list, ratings, download JARs (in a separate "jar" thread-pool), calculate codesize, remove old participants and generate a local participants list. They run in a "download" single-thread pool (except jars). Participants list and battle count are sent to a "battle generation" thread-pool, which is single-threaded.
Worker processes connect to the server and requests a battle, which is generated on-the-fly by the server in the "battle generation" thread pool. Then the worker runs the battle and sends the result back to the server. Worker processes are mono-threaded (except for threads internal to Robocode).
Server receives the result, splits it in codesize classes and sends them to an "upload" thread pool, which is currently single-threaded.
In the "upload" thread pool, results are uploaded to LiteRumble. Battle count and priority battles are downloaded and sent to the "battle generation" thread pool. If workers flood the "upload" thread pool with results, upload requests are kept in a queue, and are uploaded one at a time.
In the battle generator, participants list, battle count and priority battles are grouped and used to generate a smart battle whenever a worker requests. All battle generation logic is kept in a single class, in a single thread, making it easy to customize.
The result is you see battles going non-stop on workers, and uploads going almost non-stop in the server process, one at a time. Makes a huge difference in melee.