Developer Notes

From OHRRPGCE-Wiki
Jump to: navigation, search

Notes for OHRRPGCE developers. This page is actually directed at the core developers as much as anyone else, as there are some pretty easy to forget gotchas when working on the engine. Category:OHRRPGCE Development links to other articles relevant to developers.

FreeBASIC stuff[edit]

Non-standard use of FreeBASIC[edit]

This section lists a things which might surprise FreeBASIC programmers:

  • Source files ending in .rbas are written in RELOADBasic, which is preprocessed to FreeBASIC.
  • integer is always 32 bit (see config.bi). Before #include'ing a .bi you need to "call" the use_native_integer() macro, and then "call" the use_32bit_integer() macro afterwards.
  • Never use __FB_LINUX__, because we define it on all Unix platforms, due to shortcomings of the standard FB headers (see config.bi). Instead, use __GNU_LINUX__ if you need to test for GNU/Linux.
  • bool is an alias to integer (not boolean!) and is strictly informative. bool isn't yet used in most of the source. The defines YES and NO are used as truth values.
  • A few dark corners use calls to undocumented functions in the FB runtime library, such as fb_KeyHit, fb_FileGetIOB, and more frighteningly internal functions like fb_hStrDelTemp and hooks into file objects.
  • In some places vectors are used instead of FB's builtin arrays. vector is #defined as ptr.
  • There's a fair amount of C, C++, Objective C code for the most low level stuff (CRT and Unix .bi translations are not portable)
  • Linking is done by default using g++ instead of fbc, originally to make linking C++ work, however more recently fbc also works if you want, using scons linkgcc=0
  • Wrappers around various FB functions like DIR and KILL are used, see below
  • A fork of FB is currently used to target Android and Mac
  • Since the QB days the OHR used a whole library of assembly routines called allmodex, and none of the QB graphics or audio commands. We still only use fbgfx commands in gfx_fb.bas, and they must not be used elsewhere.

Hmm, looking over that list, every single item except allmodex and a couple wrappers are my (TMC's) fault. I just can't resist looking under the hood and playing with FB internals.

WITH[edit]

WITH is not just shorthand, it stores a pointer and uses that to access .members. Inside a 'WITH foo' block, be careful not to make the reference to 'foo' invalid, eg.:

WITH myarray(i)
 REDIM myarray(i + 10)
 'If you do this, PLACE A BIG WARNING ON THE NEXT LINE
 'Accessing myarray(i) members with .dot syntax will now likely segfault
END WITH

Converting doubles to integers[edit]

  • INT is actually the floor function. It return a double. Both unfortunate.
  • CINT returns the nearest integer to a double (rounding to even to break ties). Returns an integer
  • FIX truncates (rounds to 0). Returns a double.
  • Assigning an double to an integer variable uses CINT

INT is usually the wrong function to use.

Bugs[edit]

There are several bugs in FB that affect us. A very incomplete list:

  • fbc just loves thowing branch crossing variable definition warnings without cause, especially the linux builds. Real branch crossing warning are bugs that need to be fixed, but unfortunately most of these warnings are completely imaginary.
  • SHELL on Linux (used in findfiles and isdir) (and maybe other unices) leaks file descriptors! valgrind reports these. Luckily the limit very high.
  • OPEN on Linux can't open files which contain a \ character, it appears that the rtlib is doing something it shouldn't.
  • You'll find more if you look for FB-version-specific #ifdefs in the code

Common[edit]

UDTs[edit]

I've added warnings to all the types that are manually allocated/freed, which looks like:

'WARNING: don't add strings to this
TYPE foo
 ...
END TYPE

Don't add members of other types which need to constructed or destructed either (which is just other types containing strings). Please slap such a warning on a Type if you manually allocate/free memory for it anywhere. If you do want to add a string to such a type, you either need to replace Allocate/Deallocate with New/Delete, or to manually call .Destructor() before deleting it (strings don't actually need fancy initialisation, aside from zeroing out the string descriptor memory). I have no clue why I didn't use New/Delete everywhere in the first place; this should be cleaned up!

File functions[edit]

Use our alternatives to various FB file related functions. These add error checking and logging, sometimes work around FB bugs, and are integrated with OPENFILE/live-previewing

  • openfile should be used instead of OPEN when opening a file that might be an unlumped .rpg or .rpgdir lump file; it's always safe to use openfile instead of open. And you don't need to call FREEFILE
  • fileisreadable, fileiswriteable, diriswriteable
  • findfiles or isfile or isdir instead of DIR
    • isfile is an alias for fileisreadable. Use real_isfile otherwise
  • killdir instead of RMDIR
  • makedir instead of MKDIR
  • safekill instead of KILL (better to use safekill even if you're sure the file exists)
  • safe_shell instead of SHELL
Note: by default safe_shell times out after some seconds. If you're running something for a long time, you should probably call spawn_and_wait instead.
There are also several other alternatives to safe_shell, including run_and_get_output and spawn_and_wait.
  • And more file/system functions (which don't necessarily always replace the FB equivalent)
  • If you want to know the number of bytes successfully read from the file (like C's read()), use fb_FileGetIOB (See lumpfile.bas for example)

A note on files[edit]

FB's file functions are mostly wrappers around the C runtime, but add a lot of additional stuff. Unlike in C, reading off the end of a file succeeds, reading zeroed out bytes (even if using fb_FileGetIOB; which returns the number of bytes actually read, not the amount written to memory). However, writing off the end of a file will still write garbage between the end of the existing file and the start of the new data. You can use the extendfile Sub to guard against this before writing.

Arrays[edit]

FB arrays are very limited. (It's a little better in more recent FB versions, where you can finally put dynamic length arrays in UDTs.) See vectors for documentation of our custom arrays library.

Custom[edit]

ESC[edit]

PageUp+PageDown+Esc/Alt+F4/Cmd+Q/etc. work by setting closerequest=YES, either by directly checking for that key combo, or when the gfx backend calls post_terminate_signal().

While closerequest is true, keyval(scESC) is forced to 7, a continuous stream of new ESC keypresses until setquitflag NO is called. All menus in Custom should exit on a Esc keypress, except for those with unsaved state (eg. a modified help file and the Save/Save+Quit/Discard+Quit game menu). You need to call setquitflag NO before querying the user whether to save if an Esc keypress will cancel the attempted exit.

Debugging[edit]

We usually use gdb to debug the OHR. Many other programs are compatible with gdb and provide a graphical interface to gdb, such as ddd.

Run gdb{game,custom}.{sh,bat} to launch Game or Custom respectively under gdb, automatically breaking if FB catches an error such as an out-of-bounds array access and tries to quit (otherwise the program will cleanly exit without a backtrace)

Run val{game,custom}.sh to launch Game or Custom under valgrind, to catch memory access errors or memory leaks. I recommend compiling with scons valgrind=1, which runs faster under valgrind and is more likely to catch vector errors.

It's slightly helpful to add the following lines to your ~/.gdbinit file:

set extension-language .bas c++
set extension-language .rbas c++
set extension-language .bi c++

There is also a library of functions in misc/gdbscripts for inspecting FB arrays. The gdbgame/gdbcustom scripts automatically include these. For convenience, a few important in-game arrays have pointers to them defined, which are much easier to inspect without need to use these scripts. For example, retvalsp is equal to @retvals(0).

See Also[edit]