Carmack wrote: |
I’m not a cell phone guy. I resisted getting one at all for years, and even now I rarely carry it. To a first approximation, I don’t really like talking to most people, so I don’t go out of my way to enable people to call me. However, a little while ago I misplaced the old phone I usually take to Armadillo, and my wife picked up a more modern one for me. It had a nice color screen and a bunch of bad java game demos on it. The bad java games did it.
I am a big proponent of temporarily changing programming scope every once in a while to reset some assumptions and habits. After Quake 3, I spent some time writing driver code for the Utah-GLX project to give myself more empathy for the various hardware vendors and get back to some low-level register programming. This time, I decided I was going to work on a cell phone game. I wrote a couple java programs several years ago, and I was left with a generally favorable impression of the language. I dug out my old “java in a nutshell” and started browsing around on the web for information on programming for cell phones. After working my way through the alphabet soup of J2ME, CLDC, and MIDP, I’ve found that writing for the platform is pretty easy. In fact, I think it would be an interesting environment for beginning programmers to learn on. I started programming on an Apple II a long time ago, when you could just do an “hgr” and start drawing to the screen, which was rewarding. For years, I’ve had misgivings about people learning programming on Win32 (unix / X would be even worse), where it takes a lot of arcane crap just to get to the point of drawing something on the screen and responding to input. I assume most beginners wind up with a lot of block copied code that they don’t really understand. All the documentation and tools needed are free off the web, and there is an inherent neatness to being able to put the program on your phone and walk away from the computer. I wound up using the latest release of NetBeans with the mobility module, which works pretty well. It certainly isn’t MSDev, but for a free IDE it seems very capable. On the downside, MIDP debugging sessions are very flaky, and there is something deeply wrong when text editing on a 3.6 ghz processor is anything but instantaneous. I spent a while thinking about what would actually make a good game for the platform, which is a very different design space than PCs or consoles. The program and data sizes are tiny, under 200k for java jar files. A single texture is larger than that in our mainstream games. The data sizes to screen ratios are also far out of the range we are used to. A 128x128x16+ bit color screen can display some very nice graphics, but you could only store a half dozen uncompressed screens in your entire size budget. Contrast with PCs, which may be up to a few megabytes of display data, but the total game data may be five hundred times that. You aren’t going to be able to make an immersive experience on a 2” screen, no matter what the graphics look like. Moody and atmospheric are pretty much out. Stylish and fun is about the best you can do. The standard cell phone style discrete button direction pad with a center action button is a good interface for one handed navigation and selection, but it sucks for games, where you really want a game boy style rocking direction pad for one thumb, and a couple separate action buttons for the other thumb. These styles of input are in conflict with each other, so it may never get any better. The majority of traditional action games just don’t work well with cell phone style input. Network packet latency is bad, and not expected to be improving in the foreseeable future, so multiplayer action games are pretty much out (but see below). I have a small list of games that I think would work out well, but what I decided to work on is DoomRPG – sort of Bard’s Tale meets Doom. Step based smooth sliding/turning tile movement and combat works out well for the phone input buttons, and exploring a 3D world through the cell phone window is pretty neat. We talked to Jamdat about the business side of things, and hired Fountainhead Entertainment to turn my proof-of-concept demo and game plans into a full-featured game. So, for the past month or so I have been spending about a day a week on cell phone development. Somewhat to my surprise, there is very little internal conflict switching off from the high end work during the day with gigs of data and multi-hundred instruction fragment shaders down to texture mapping in java at night with one table lookup per pixel and 100k of graphics. It’s all just programming and design work. It turns out that I’m a lot less fond of Java for resource-constrained work. I remember all the little gripes I had with the Java language, like no unsigned bytes, and the consequences of strong typing, like no memset, and the inability to read resources into anything but a char array, but the frustrating issues are details down close to the hardware. The biggest problem is that Java is really slow. On a pure cpu / memory / display / communications level, most modern cell phones should be considerably better gaming platforms than a Game Boy Advanced. With Java, on most phones you are left with about the CPU power of an original 4.77 mhz IBM PC, and lousy control over everything. I spent a fair amount of time looking at java byte code disassembly while optimizing my little rendering engine. This is interesting fun like any other optimization problem, but it alternates with a bleak knowledge that even the most inspired java code is going to be a fraction the performance of pedestrian native C code. Even compiled to completely native code, Java semantic requirements like range checking on every array access hobble it. One of the phones (Motorola i730) has an option that does some load time compiling to improve performance, which does help a lot, but you have no idea what it is doing, and innocuous code changes can cause the compilable heuristic to fail. Write-once-run-anywhere. Ha. Hahahahaha. We are only testing on four platforms right now, and not a single pair has the exact same quirks. All the commercial games are tweaked and compiled individually for each (often 100+) platform. Portability is not a justification for the awful performance. Security on a cell phone is justification for doing something, but an interpreter isn’t a requirement – memory management units can do just as well. I suspect this did have something to do with Java’s adoption early on. A simple embedded processor with no MMU could run arbitrary programs securely with java, which might make it the only practical option. However, once you start using blazingly fast processors to improve the awful performance, a MMU with a classic OS model looks a whole lot better. Even saddled with very low computing performance, tighter implementation of the platform interface could help out a lot. I’m not seeing very conscientious work on the platforms so far. For instance, there is just no excuse for having 10+ millisecond granularity in timing. Given that the java paradigm is sort of thread-happy anyway, having a real scheduler that Does The Right Thing with priorities and hardware interfacing would be an obvious thing. Pressing a key should generate a hardware interrupt, which should immediately activate the key listening thread, which should be able to immediately kill an in-process rendering and restart another one if desired. The attitude seems to be 15 msec here, 20 there, stick it on a queue, finish up a timeslice, who cares, right? I suspect I will enjoy working with BREW, the competing standard for cell phone games. It lets you use raw C/C++ code, or even, I suppose, assembly language, which completely changes the design options. Unfortunately, they only have a quarter the market share that the J2ME phones have. Also, the relatively open java platform development strategy is what got me into this in the first place – one night I just tried writing a program for my cell phone, which isn’t possible for the more proprietary BREW platform. I have a serious suggestion for the handset designers to go with my idle bitching. I have been told that fixing data packet latency is apparently not in the cards, and it isn’t even expected to improve much with the change to 3G infrastructure. Packet data communication seems more modern, and has the luster of the web, but it is worth realizing that for network games and many other flashy Internet technologies like streaming audio and video, we use packets to rather inefficiently simulate a switched circuit. Cell phones already have a very low latency digital data path – the circuit switched channel used for voice. Some phones have included cellular modems that use either the CSD standard (circuit switched data) at 9.8Kbits or 14.4Kbits or the HSCSD standard (high speed circuit switched data) at 38.4Kbits or 57.6Kbits. Even the 9.8Kbit speed would be great for networked games. A wide variety of two player peer-to-peer games and multiplayer packet server based games could be implemented over this with excellent performance. Gamers generally have poor memories of playing over even the highest speed analog modems, but most of the problems are due to having far too many buffers and abstractions between the data producers/consumers and the actual wire interface. If you wrote eight bytes to the device and it went in the next damned frame (instead of the OS buffer, which feeds into a serial FIFO, which goes into another serial FIFO, which goes into a data compressor, which goes into an error corrector, and probably a few other things before getting into a wire frame), life would be quite good. If you had a real time scheduler, a single frame buffer would be sufficient, but since that isn’t likely to happen, having an OS buffer with accurate queries of the FIFO positions is probably best. The worst gaming experiences with modems weren’t due to bandwidth or latency, but to buffer pileup. |
Cyan~Fire wrote: |
not being able to parse an array like I can in C++. |
Cyan~Fire wrote: |
not being able to compare instances (that is, the pointers to them) of classes in a switch statement or even just an if statement. |
Cyan~Fire wrote: |
having to implement a class just to get some kind of callback (for ActionListener or the like) instead of just making a stupid function. |
Cyan~Fire wrote: |
having to type "new" every time I want to make a stupid class. |
Cyan~Fire wrote: |
not having readily available unsigned types. |
Cyan~Fire wrote: |
not being able to easily access the bits in a variable. |
Cyan~Fire wrote: |
not being able to take a file and read it into a struct/structs in one, nice operation. |
Cyan~Fire wrote: |
the idea of making a whole new copy of a string to change one character |
Cyan~Fire wrote: |
not having a good string parser available like sscanf(). (At least, I don't remember one. Please tell me if there is one. |
Cyan~Fire wrote: |
having the compiler decide if I want a parameter to be by value or by reference. |
Code: Show/Hide unsigned int n_trigs;
Trigger *t; bool good = (ret == ERR_none); if (good) { fread(&n_trigs, sizeof(long), 1, dcin); triggers.allocate(n_trigs, true); //probably can't pre-allocate space like that in Java t = triggers.first(); } if (n_trigs && good) { i = n_trigs; while (i-- && good) good = t++->read(dcin); //definitely not if (good && !feof(dcin)) { t_order.allocate(n_trigs, true); fread(t_order.first(), sizeof(long), n_trigs, dcin); //read the whole array at once? nope. if (*t_order.first() > n_trigs) ret = ERR_trig; } else ret = ERR_trig; } |
Code: Show/Hide extern Scenario scen; |
Code: Show/Hide Scenario scen; |
Code: Show/Hide union weaponInfo
{ struct { Uint16 type : 5; // enum Projectile_Types Uint16 level : 2; // Only for bombs/bullets Uint16 shrapBounce : 1; // Bouncing shrapnel? Uint16 shrapLevel : 2; // Shrapnel level 0..3 Uint16 shrapCount : 5; // 0-31 Uint16 fireType : 1; // Bombs -> Mines, Bullets -> Multifire }; Uint16 n; }; |
Cyan~Fire wrote: | |
Parsing arrays: (actually, this contains a few of Java's incapabilities)
I know the code is messy, but it's nowhere near as messy as the file format it's reading. ![]() |
Cyan~Fire wrote: |
Comparing pointers: maybe that's one of the things that I've forgotten. Can you do it in a switch? |
Cyan~Fire wrote: |
Need to know how to use function pointers? Not if you're just using someone else's callback mechanism (which you essentially are in Java, anyway). And the excuse that since it's hard, it shouldn't be available is bunk. |
Cyan~Fire wrote: | ||
Making an instantiation of a class? I type
|
Cyan~Fire wrote: | |
About bits, I mean that last time I checked you can't do this:
|
Cyan~Fire wrote: |
My point about Strings is that I have no clue why they're immutable. It seems rather silly. |
Cyan~Fire wrote: |
Formatted input: Eh, but probably not a way I like better than sscanf(). If it's something like C++ iostream insertion/extraction, I won't like it. |
Cyan~Fire wrote: |
Function parameters: When I learned Java, I'm sure the integral types were by value. Is that not the case anymore? |
Quote: |
not being able to take a file and read it into a struct/structs in one, nice operation. |
Dr Brain wrote: |
FUCK DUDE! That's why C++ sucks. If you can't understand a segment of code in 30 seconds it's wrong. Get a better example or comment your code or SOMETHING. I still have no fucking idea what you're trying to do. |
Dr Brain wrote: |
Hard? That's not the reason. Dangerous is the reason. |
Dr Brain wrote: |
Declare your methods static and you get the same effect without even having to declare the variable. I find it hard to believe that you use globals like that, though. |
Dr Brain wrote: |
Nope. You can't. It's still easy enough to make a class to do that, though. Only reason to do it like that is for direct output to a network, as memory isn't a good reason. |
Dr Brain wrote: |
Iostream is an integral part of C++. |
Dr Brain wrote: |
It's still the case, but the compiler doesn't decide that. |
Bak wrote: |
you can use the primative wrapper classes (Integer, Short, Character, ect.) if you want to pass by reference... or the primiatives if you want to pass by value. |
Bak wrote: |
If you want a modifiable string then use a character array. You can print it out to a BufferedWriter (like System.out) using a print or println method... just list Strings. |
Bak wrote: |
Java has a great way to do this, using XMLEncoders and XMLDecoders |
Cyan~Fire wrote: |
Sorry, I wanted to use a piece of code that I'm serious about, not some example. You don't need to understand every function call, just the fact that I'm pre-allocating memory for something, parsing an array using pointers, and reading an array directly from a file. If you don't know fread(), well, that's not my problem. ![]() |
Cyan~Fire wrote: |
Hard to make it safe. Whatever. Even Java apps can have bugs. |
Cyan~Fire wrote: |
A class with all static methods will automatically have a static instantiation? If so, I'll admit that's pretty smart, but kinda messy. |
Cyan~Fire wrote: |
I agree with everything you just said, and yet my point still stands. |
Cyan~Fire wrote: |
I don't get your point. That doesn't mean I have to use it. |
Cyan~Fire wrote: |
Then what does? (I know about the wrapper classes, I'm not saying you can't pass an int by reference, it's just a workaround.) |
Cyan~Fire wrote: |
I know. In C++ I can put an '&' next to the variable and it's by reference. |
Cyan~Fire wrote: |
Yes, there are workarounds. It's still rather silly. |
Quote: |
Yes, but they're not as prone to hacking as C/C++ |
Quote: |
Iostream is an integral part of C++. |
Dr Brain wrote: |
They don't all have to be static. |
Dr Brain wrote: |
No, you don't have to use it. You can't really talk about C++ doing a better job, though. |
Dr Brain wrote: |
The language dictates it, not the compiler. |
Dr Brain wrote: |
And why do you do that? For no real reason, right? The only time to use by value is on primitives. Everything else is more efficient to pass by reference. If you want a copy (and how many times do you in practice?) just make one explicitly. |
Dr Brain wrote: |
Workarounds? Char arrays? Yuck. |
Dr Brain wrote: |
I'm glad they're immutable personally. I don't like passing strings and having their contents changed. |
50% wrote: |
also I don't think its entirely made in Java. |
Cyan~Fire wrote: |
Pre-allocate: It allocates the space for a dynamic array of objects (and optionally constructs them) so that you don't waste CPU time having the class continuously expand itself if you're copying a lot of items. |
Cyan~Fire wrote: |
Parsing an array: Initialize a pointer to the first member of an array, and then do an operation and increment the pointer until you reach the end. Saves some messy indexing and the like. |
Cyan~Fire wrote: |
Indeed I agree with 50% about security. If you can't handle sizeof(), you can't handle programming. |
Cyan~Fire wrote: |
Sorry, I'm not getting this. Could you explain how you do the extern thang in Java? (My original point was that you could create an instance of a class easily, without having to mess around with allocating memory.) |
Cyan~Fire wrote: |
My point about the bits is that, yes, you can make Java work around its incapabilities here, but it's much more natural in C++. |
Cyan~Fire wrote: |
I said it before: fprintf(), fscanf(), sprintf(), sscanf(). You can't beat 'em. C++ is just that. C plus some. There's nothing that dictates "you can't use the old C stuff because this is C++." |
Cyan~Fire wrote: |
Well no, it's the writers of the language, not the language itself. FFS, have you heard of synecdoche? |
Cyan~Fire wrote: |
I decide. |
Cyan~Fire wrote: |
It's all char arrays anyway, whether apparent or not. There exist quite a few C++ string classes that essentially mimic the Java but without the restriction of immutability. |
Quote: |
*An argument for the immutability of strings: In practice, strings are frequently shared between clients and thus frequentlythe crux of defects that emerge when one client inadvertently affects another. For example, a method that returns a customer's name as a string will typically retain its reference to the name. If the client, say, uppercases the string to use it in a hash table, the Customer object's name would change as well, if not for the immutability of strings. In java, you can produce an uppercase version of a string, but this must be a new object, not an altered version of the initial string. The immutability of strings make them safe to share among multiple clients.
* Against: The immutability of strings protects us from certain errors but at a heavy price. First, developers are cut off from any ability to change a string, regardless of how we might justify this need. Second, adding special rules to a language makes the language more difficult to learn and to use. Java is far, far more difficult to learn than the equally powerful Smalltalk language. Finally, no computer language can keep me from making errors. I'd be much better off i you let me learn the language quickly, so I have time to also learn how to set up and use a testing framework. |
Dr Brain wrote: |
Iterators. They're already an integral part of the collection framework. |
Dr Brain wrote: |
Static variables are initialized at start. Static methods can use static variables. Static methods can be called without a class instance. |
Dr Brain wrote: |
Yes, the thing is you only rarely have to manipulate bits in Java. |
Dr Brain wrote: |
There are some things you can't do in C++ that you could do with C. Something involving void pointers, I don't recall the specifics. |
Dr Brain wrote: |
C++ doesn't let you choose if a command is compiled with an immediate operand or not, yet I don't see you complaining. That's a compiler level choice, not a language level one. |
Dr Brain wrote: |
Yes, but why do you need to decide? Deciding insignificant details only takes time away from real programming. |
Dr Brain wrote: |
I still don't see why immutability is bad. |
Dr Brain wrote: |
No language can prevent all errors, but they can relieve pressure on the programmer. You don't have to always think about what sort of input a hacker might try to shove down your throat. |
Quote: |
You're talking about saving time?? When you have to deal with wrapping primitive types in classes and creating new copies of objects if you want to modify them without affecting the original?? |
Dr Brain wrote: |
Well, I cleared up a few of your misunderstandings, so I think it was a worthwhile discussion.
<shakes cyan's hand> |
Quote: |
Hope for Unsigned Primitive Types and Operator Overload?
One man remarked: "I'm haunted by my C++ background. Is there any hope for unsigned primitive types and operator overload?" To which Gosling replied: "Operator overloading is interesting. I'm personally a fan of it. The problem is that if you look at C++ -- and I'm also a C++ refugee -- operator overloading was frequently used inappropriately. I did a number of surveys about operator overloading and there were three camps. 5-10% of the world thought it was really great; about 50% didn't really care. And the rest of the world threatened to come after me with machetes. There are some things that are easier to misuse than others and operator overloading is one of them. I've struggled to come up with a way to do operator overloading that was harder to abuse and made several proposals in the last few years, but have not made any progress in making anyone happy." Eric Lindholm compared operator overloading to macros and said there may be five good things you can do with them. "There are things around numerics that you would like to have operator overloading for, but beyond that, it is just nasty," he asserted. Graham Hamilton put the issue in a larger perspective: "James created a masterpiece with the Java language. It was a brilliant tradeoff of power and simplicity. It's like Da Vinci created the 'Mona Lisa,' and we want to keep the crayons away from him and let it be. We want this masterpiece to survive." While expressing caution about the core structure of the Java language, Hamilton encouraged developers to build new languages. "I actually would like to see new languages on top of the Java platform. The Java language has one design center. There are other design centers where we could have other languages doing different kinds of things on top of the platform." Gosling saw a place for operator overloading in a language for mathematics: "If I was going to do a language for mathematics, which I often wish for the spare time to do, I would do a lot of things radically differently. That is mostly where you see the desire for operator overloading. Unsigned operator overloading is a really interesting case. I did a lot of statistical surveys about people's software to figure out what they used unsigned operator overloading for. They were all over the map. A fair number of people used it to get an extra bit of precision for fairly primitive Boolean operations. One of the problems with unsigned is that if you walk up to a developer and give them a piece of arithmetic and give them puzzles assigning unsigned types to things, and ask them what this does, almost no one can get anything right except comparison, addition and assignment. If you start combining comparison and addition, no one gets it right. You can do almost nothing with unsigned numbers before they turn into something that your CPU can't handle. About the only thing that was truly useful was unsigned right shift. There is an unsigned right shift operator. That is the one good thing I saw people doing with unsigned. You get into immediate trouble with unsigned as soon as you try to multiply them." Hamilton reiterated the point that while it is possible to add individual features to the core Java platform that are useful, there is a danger in making the programs so complicated that they are hard to understand. |
Quote: |
Gosling: I think in any kind of design, you must drive for simplicity all the time. If you don't, complexity will nail you. Dealing with complexity is hard enough.
In programming language design, one of the standard problems is that the language grows so complex that nobody can understand it. One of the little experiments I tried was asking people about the rules for unsigned arithmetic in C. It turns out nobody understands how unsigned arithmetic in C works. There are a few obvious things that people understand, but many people don't understand it. So one of the most important criteria for judging a design for me is the manual. Is the manual out of control, or is it reasonably concise? You can write a pretty decent Java manual in less than 100 pages. The current Java language spec is pretty thick, but that's because it's probably the most detailed language spec ever written. It goes through all of the details. I couldn't write the Java language spec. |
Quote: |
2) Lack of structs (as cyan pointed out) |
Cerium wrote: |
For the record...
Everything in java is passed by value, nothing is passed by reference. Objects are essentially pointers. When you pass an object to a function, youre passing the reference to that object by value. |
Dr Brain wrote: |
In that case, even in C++, there is no such thing as by reference. References are essentially pointers. When you pass a reference to a function, you're passing the pointer to that object by value.
|
Dr Brain wrote: |
You're wrong ![]() |
Bak wrote: |
structs are just classes with everthing public, which is quite possible in Java |
CypherJF wrote: |
java passes by value -> copy reference. (see below by dr brain)
public void blah(int []a) { a = new int[3]; } meaning the address of 'a' is locked, it wont be changed. etc. etc. |
Dr Brain wrote: |
<insert internet anger here> |