1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
|
Introduction
This file documents the proxy windowing interface. It is far from complete.
Contents:
I. An overview of the components
II. Adding a standard procedure
III. Adding a standard callback
IV. Adding an extension
I. An overview of the components
The proxy window interface is made up of a number of components. This is
because it is intended to be used both as a standard window interface to
be linked with the game executable and as a library that can be linked
with an external window port to handle the communication with the game.
The modules can be broken down into the following types:
A. Common modules used in both processes (proxycom)
B. The proxy window interface (winproxy)
C. Utility modules used in the game executable (proxyutl)
D. Client modules used only by the external process (proxyclnt)
The main game executable is built with the winproxy, proxyutl and proxycom
modules; external interfaces are built with the nhproxy library which
consists of the proxyclnt and proxycom modules.
When building a window interface as an external port, the proxyutl modules
are just a special case of the winproxy modules. However, if a window
interface is designed to be built as either an external port or as an
internal window interface then the code can normally be simplified by
using the proxyutl modules in the internal window interface.
The proxy window interface has a number of include files which are used
as follows:
A. Common include files:
nhxdr.h
proxycom.h
B. Proxy window interface include files:
winproxy.h
C. External process include files:
proxyclnt.h
proxycb.h
II. Adding a standard procedure
First choose a name for the new procedure. We will use ${name} to refer to
the lowercase version of the chosen name in this document and ${NAME} to
refer to the uppercase version.
Modify the source as follows:
A. win/proxy/ext_protocol.html
o Add a new procedure ID to the list using the next unused ID number.
o Add a new section describing the procedure, its parameters and result.
o Bump the version of the protocol, NhExt, if necessary.
o Note the formal description of parameters and results must follow
the syntax of Sun's XDR language so that the test procedure laid
out in win/proxy/Makefile.* will be valid. Formal descriptions must
be enclosed in a <pre>...</pre> pair. These html tags must be on a
line by themselves.
o If you have access to rpcgen then you can use the relevant makefile
to build rpcgen-nhext_xdr.c and so check your syntax.
B. include/prxyclnt.h
o Add a new field to the end of struct window_ext_procs to hold a pointer
to a function which takes the relevant parameters and returns the
relevant result. The field should be named winext_${name}.
C. include/proxycom.h
o Add a new procedure ID to the list using the ID chosen above:
#define EXT_FID_${NAME} 0x##
o If any of the parameters or return value are too complex for nhext_rpc()
then define a struct proxy_${name}_req and/or proxy_${name}_res
and declare an XDR function to handle it (enclose the additions
inside an #ifdef NHXDR_H ... #endif pair).
o If you changed the NhExt protocol version above, then update the
values here to match.
D. win/proxy/compxdr.c
If you declared an XDR function in winproxy.h, then you need to define
it in this file. Many XDR functions consist simply of calling standard
XDR functions for each field of the aggregate type and returning the
bitwise AND of their return values. XDR functions should not return
early if an error occurs so the use of logical AND operators is
generally to be avoided.
E. win/proxy/winproxy.c
Define the proxy_${name} function you declared above. This function will
be called by the game when it wants to perform the relevant procedure.
It should:
o Do any preparation needed on the parameters (normally none).
o Initialize any buffers that will be used to receive values back from
the windowing interface. The XDR convention is that NULL pointers
indicate that XDR should allocate memory whereas non-NULL pointers
indicate that XDR should use the pre-allocated memory.
o Call nhext_rpc() to send an RPC packet to the windowing interface and
read back the reply.
o Return the result (if any).
F. win/proxy/proxysvc.c
This module implements the support in the windowing interface(s) for the
procedure. You should:
o Declare a static function to handle the incoming request as follows:
static void FDECL(proxy_svc_${name}, \
(unsigned short, NhExtXdr *, NhExtXdr *));
o Define the handling function. It should:
- Decode the incoming parameters by calling nhext_rpc_params() and
passing it the request XDR handle and parameters which describe the
format of the parameters to your procedure.
- Take whatever action the game is requesting.
- Encode the result of that action ready to be sent back to the game
by calling nhext_rpc_params() and passing it the reply XDR handler
and parameters which describe the format of the results of your
procedure. Note: If this procedure has no result then you must
still return an empty result to the game.
o Add an entry to the services array as follows:
EXT_FID_${NAME}, proxy_svc_${name},
o If this is an asynchronous procedure (this is, if it returns no results
and there is no need for the remote end to wait for the procedure to
complete) then add an entry for the procedure in the async_procedures[]
array. If there are already asynchronous procedures with IDs in the
same range (1-32, 33-64, ...) then just add the bit corresponding to
the new procedure to the mask for that range. If this is the first
asynchronous procedure in the range then add a new mask as follows:
1 << EXT_FID_$(NAME) - EXT_FID_$(BASE_NAME),
where $(BASE_NAME) is the name of the first procedure in the range
(whether asynchronous or not).
G. external window interface(s)
Add an entry to the window_ext_procs structure for each external window
interface for the newly defined winext_${name} field. This should either
be a hook function that does nothing except return an appropriate result
(for window interfaces that do not wish to support the new procedure) or
a function to do the work for the new procedure as newly documented in
ext_protocol.html.
H. game source
Add code to call proxy_${name} at the appropriate points in the game source.
III. Adding a standard callback
First choose a name for the new callback. We will use ${name} to refer to
the lowercase version of the chosen name in this document and ${NAME} to
refer to the uppercase version.
Modify the source as follows:
A. win/proxy/ext_protocol.html
o Add a new callback ID to the list using the next unused ID number.
o Add a new section describing the callback, its parameters and result.
o Bump the version of the protocol, NhExt, if necessary.
o Note the formal description of parameters and results must follow
the syntax of Sun's XDR language so that the test procedure laid
out in win/proxy/Makefile.* will be valid. Formal descriptions must
be enclosed in a <pre>...</pre> pair. These html tags must be on a
line by themselves.
o If you have access to rpcgen then you can use the relevant makefile
to build rpcgen-nhext_xdr.c and so check your syntax.
B. include/proxycb.h
o Declare proxy_cb_${name} as a function which takes the relevant
parameters and returns the relevant result.
C. include/proxycom.h
o Add a new callback ID to the list using the ID chosen above:
#define EXT_CID_${NAME} 0x##
o If any of the parameters or return value are too complex for nhext_rpc()
then define a struct proxycb_${name}_req and/or proxycb_${name}_res
and declare an XDR function to handle it (enclose the additions
inside an #ifdef NHXDR_H ... #endif pair).
o If you changed the NhExt protocol version above, then update the
values here to match.
D. win/proxy/compxdr.c
If you declared an XDR function in proxycb.h, then you need to define
it in this file. Many XDR functions consist simply of calling standard
XDR functions for each field of the aggregate type and returning the
bitwise AND of their return values. XDR functions should not return
early if an error occurs so the use of logical AND operators is
generally to be avoided.
E. win/proxy/proxycb.c
Define the proxy_cb_${name} function you declared above. This
function will be called by windowing interfaces when they want
to perform the relevant callback. It should:
o Do any preparation needed on the parameters (normally none).
o Initialize any buffers that will be used to receive values back
from the game. The XDR convention is that NULL pointers indicate
that XDR should allocate memory whereas non-NULL pointers indicate
that XDR should use the pre-allocated memory.
o Call nhext_rpc() to send an RPC packet to the game and read back
the reply.
o Return the result (if any).
F. win/proxy/callback.c
This module implements the support in the game executable for the
call back. You should:
o Declare a static function to handle the incoming request as follows:
static void FDECL(callback_${name}, \
(unsigned short, NhExtXdr *, NhExtXdr *));
o Define the handling function. It should:
- Decode the incoming parameters by calling nhext_rpc_params() and
passing it the request XDR handle and parameters which describe the
format of the parameters to your call back.
- Take whatever action the calling windowing interface is requesting.
- Encode the result of that action ready to be sent back to the
windowing port by calling nhext_rpc_params() and passing it the
reply XDR handler and parameters which describe the format of the
results of your call back. Note: If this call back has no result
then you must still return an empty result to the windowing port.
o Add an entry to the proxy_callbacks array as follows:
EXT_CID_${NAME}, callback_${name},
G. win/proxy/winproxy.c
If this is an asynchronous callback (this is, if it returns no results
and there is no need for the remote end to wait for the callback to
complete) then add an entry for the callback in the async_callbacks[]
array. If there are already asynchronous callbacks with IDs in the
same range (1-32, 33-64, ...) then just add the bit corresponding to
the new callback to the mask for that range. If this is the first
asynchronous callback in the range then add a new mask as follows:
1 << EXT_CID_$(NAME) - EXT_CID_$(BASE_NAME),
where $(BASE_NAME) is the name of the first callback in the range
(whether asynchronous or not).
H. external window interface(s)
Add code to call proxy_cb_${name} to the external window interfaces which
need the new callback.
IV. Adding an extension
Extensions are used to allow callbacks not detailed in the NhExt standard
to be provided by a game server and queried for by the client. Extensions
have names, version numbers and a set of callbacks. It is assumed that
by one means or another the name and version number is sufficient for the
client and server to agree on the meaning of each callback along with the
details of the various request and reply packets (the extension system
provides no means of querying these details).
First choose a name for the new extension. We will use ${name} to refer to
the chosen name in this document.
Modify the source as follows:
A. server
Add code in some appropriate module (or create a new module) to handle the
extension. This code should include an initialization routine and a handler.
The initialization routine is called by proxy_init() during the initialization
of the proxy interface. It is passed one parameter - the base ID chosen for the
extension (this may vary as extensions are added or removed). The handler will
be called to handle a callback. It is passed the ID of the callback and pointers
to the request and reply packets. The following is an example of a trivial
extension with just one callback which takes one integer value and returns
value + 1:
static unsigned short xyzzy_base_id;
void
xyzzy_init(base_id)
unsigned short base_id;
{
xyzzy_base_id = base_id;
}
void
xyzzy_handler(id, request, reply)
unsigned short id;
NhExtXdr *request, *reply;
{
int i;
switch (id - xyzzy_base_id)
{
case 0:
nhext_rpc_params(request, 1, EXT_INT_P(i));
i++;
nhext_rpc_params(reply, 1, EXT_INT(i));
break;
default:
impossible("xyzzy_handler: Bad ID %u (base %u)",
id, xyzzy_base_id);
nhext_rpc_params(reply, 0);
}
}
B. win/proxy/winproxy.c
o Declare the initialization and handler functions you defined above before
the definition of proxy_extents[], as follows:
extern void FDECL(${name}_init, (unsigned short));
extern void FDECL(${name}_handler,
(unsigned short, NhExtXdr *, NhExtXdr *));
o Add an entry to proxy_extents[] as follows:
{ "${name}", "1.0", ${name}_init, 2, ${name}_handler },
- Replace 1.0 with the version number. If you need to support more than
one version of an extension then you must supply seperate initialization
functions although they can share the same handler function if desired.
The prefered interface should be listed first.
- Replace 2 with the number of callbacks included in this extension.
|