 |
Crossfire Server, Trunk
1.75.0
|
Go to the documentation of this file.
72 #define PYTHON_CACHE_SIZE 256
77 struct pycode_cache_entry {
84 #define MAX_COMMANDS 1024
98 vsnprintf(
buf,
sizeof(
buf), fmt, arg);
101 PyErr_SetString(PyExc_ValueError,
buf);
120 if (!PyArg_ParseTuple(args,
"i", &eventcode))
133 if (!PyArg_ParseTuple(args,
"i", &eventcode))
157 if (!PyArg_ParseTuple(args,
"s", &obname))
170 return Py_BuildValue(
"i", i);
183 if (!PyArg_ParseTuple(args,
"i", &i))
196 if (!PyArg_ParseTuple(args,
"ss", &premiere, &seconde))
201 return Py_BuildValue(
"i", 1);
203 return Py_BuildValue(
"i", 0);
206 static PyObject *
findPlayer(PyObject *
self, PyObject *args) {
211 if (!PyArg_ParseTuple(args,
"s", &txt))
224 static PyObject *
readyMap(PyObject *
self, PyObject *args) {
230 if (!PyArg_ParseTuple(args,
"s|i", &mapname, &
flags))
238 static PyObject *
createMap(PyObject *
self, PyObject *args) {
243 if (!PyArg_ParseTuple(args,
"ii", &sizex, &sizey))
293 static PyObject *
getWhoAmI(PyObject *
self, PyObject *args) {
330 return Py_BuildValue(
"");
351 static PyObject *
getEvent(PyObject *
self, PyObject *args) {
386 std::vector<archetype *> archs;
391 list = PyList_New(0);
392 for (
auto arch : archs) {
398 static PyObject *
getPlayers(PyObject *
self, PyObject *args) {
406 list = PyList_New(0);
413 static PyObject *
getMaps(PyObject *
self, PyObject *args) {
415 std::vector<mapstruct *>
maps;
421 list = PyList_New(0);
422 for (
auto map :
maps) {
428 static PyObject *
getParties(PyObject *
self, PyObject *args) {
430 std::vector<partylist *> parties;
435 list = PyList_New(0);
436 for (
auto party : parties) {
442 static PyObject *
getRegions(PyObject *
self, PyObject *args) {
449 list = PyList_New(0);
463 list = PyList_New(0);
481 context->
third = NULL;
483 context->
event = NULL;
486 snprintf(context->
options,
sizeof(context->
options),
"%s", params);
507 if (!PyArg_ParseTuple(args,
"ssd|i", &cmdname, &scriptname, &cmdspeed, &
type))
532 set_exception(
"failed to register command (overriding an existing one with a different type?)");
540 static PyObject *
getTime(PyObject *
self, PyObject *args) {
548 list = PyList_New(0);
549 PyList_Append(
list, Py_BuildValue(
"i", tod.
year));
550 PyList_Append(
list, Py_BuildValue(
"i", tod.
month));
551 PyList_Append(
list, Py_BuildValue(
"i", tod.
day));
552 PyList_Append(
list, Py_BuildValue(
"i", tod.
hour));
553 PyList_Append(
list, Py_BuildValue(
"i", tod.
minute));
556 PyList_Append(
list, Py_BuildValue(
"i", tod.
season));
566 if (!PyArg_ParseTuple(args,
"i", &
id))
575 if (!PyArg_ParseTuple(args,
"s", &
name))
580 static PyObject *
findFace(PyObject *
self, PyObject *args) {
584 if (!PyArg_ParseTuple(args,
"s", &
name))
595 if (!PyArg_ParseTuple(args,
"is", &intLevel, &
message))
630 if (!PyArg_ParseTuple(args,
"s", &
name))
639 if (!PyArg_ParseTuple(args,
"i", &i))
648 if (!PyArg_ParseTuple(args,
"i", &i))
657 if (!PyArg_ParseTuple(args,
"i", &i))
666 if (!PyArg_ParseTuple(args,
"i", &i))
671 static PyObject *
addReply(PyObject *
self, PyObject *args) {
682 if (!PyArg_ParseTuple(args,
"ss", &word, &reply)) {
709 if (!PyArg_ParseTuple(args,
"s|i", &
message, &
type)) {
722 static PyObject *
npcSay(PyObject *
self, PyObject *args) {
741 cf_log(
llevError,
"CFPython: warning, too long message in npcSay, will be truncated");
755 int largest_coin = 0;
758 if (!PyArg_ParseTuple(args,
"L|i", &value, &largest_coin))
762 return Py_BuildValue(
"s",
buf);
766 {
"WhoAmI",
getWhoAmI, METH_NOARGS, NULL },
772 {
"WhatIsEvent",
getEvent, METH_NOARGS, NULL },
780 {
"ReadyMap",
readyMap, METH_VARARGS, NULL },
781 {
"CreateMap",
createMap, METH_VARARGS, NULL },
782 {
"FindPlayer",
findPlayer, METH_VARARGS, NULL },
783 {
"MatchString",
matchString, METH_VARARGS, NULL },
791 {
"GetPlayers",
getPlayers, METH_NOARGS, NULL },
793 {
"GetMaps",
getMaps, METH_NOARGS, NULL },
794 {
"GetParties",
getParties, METH_NOARGS, NULL },
795 {
"GetRegions",
getRegions, METH_NOARGS, NULL },
800 {
"GetTime",
getTime, METH_NOARGS, NULL },
804 {
"FindFace",
findFace, METH_VARARGS, NULL },
810 {
"AddReply",
addReply, METH_VARARGS, NULL },
812 {
"NPCSay",
npcSay, METH_VARARGS, NULL },
814 { NULL, NULL, 0, NULL }
825 context->
down = NULL;
845 Py_XDECREF(context->
event);
846 Py_XDECREF(context->
third);
847 Py_XDECREF(context->
who);
856 PyObject *scriptfile;
858 fd = open(filename, O_RDONLY);
861 scriptfile = PyFile_FromFd(fd, filename,
"r", -1, NULL, NULL, NULL, 1);
870 return fdopen(PyObject_AsFileDescriptor(obj),
"r");
879 #if defined(IS_PY3K9) || defined(IS_PY3K10)
884 static PyObject *io_module = NULL;
899 PyObject* empty = PyUnicode_FromString(
"");
904 PyObject_SetAttrString(
catcher,
"value", empty);
914 PyObject *scriptfile = NULL;
916 struct stat stat_buf;
920 if (stat(filename, &stat_buf)) {
921 cf_log(
llevError,
"CFPython: script file %s can't be stat'ed\n", filename);
959 if (
replace->file != sh_path) {
965 #if defined (IS_PY3K9) || defined(IS_PY3K10)
977 io_module = PyImport_ImportModule(
"io");
978 scriptfile = PyObject_CallMethod(io_module,
"open",
"ss", filename,
"rb");
980 cf_log(
llevDebug,
"CFPython: script file %s can't be opened\n", filename);
984 PyObject *source_bytes = PyObject_CallMethod(scriptfile,
"read",
"");
985 (void)PyObject_CallMethod(scriptfile,
"close",
"");
986 PyObject *
code = Py_CompileString(PyBytes_AsString(source_bytes), filename, Py_file_input);
990 if (PyErr_Occurred())
993 replace->cached_time = stat_buf.st_mtime;
1001 cf_log(
llevDebug,
"CFPython: script file %s can't be opened\n", filename);
1008 if ((n = PyParser_SimpleParseFile(pyfile, filename, Py_file_input))) {
1009 replace->code = PyNode_Compile(n, filename);
1012 if (PyErr_Occurred())
1015 replace->cached_time = stat_buf.st_mtime;
1024 Py_DECREF(scriptfile);
1027 assert(run != NULL);
1028 run->used_time =
time(NULL);
1033 PyCodeObject *pycode;
1044 dict = PyDict_New();
1045 PyDict_SetItemString(dict,
"__builtins__", PyEval_GetBuiltins());
1046 ret = PyEval_EvalCode((PyObject *)pycode, dict, NULL);
1047 if (PyErr_Occurred()) {
1070 snprintf(tmp,
sizeof(tmp),
"Crossfire_%s",
name);
1072 cst = PyModule_New(tmp);
1073 dict = PyDict_New();
1075 while (constants[i].
name != NULL) {
1076 PyModule_AddIntConstant(cst, (
char *)constants[i].
name, constants[i].value);
1077 PyDict_SetItem(dict, PyLong_FromLong(constants[i].value), PyUnicode_FromString(constants[i].
name));
1080 PyDict_SetItemString(PyModule_GetDict(module),
name, cst);
1082 snprintf(tmp,
sizeof(tmp),
"%sName",
name);
1083 PyDict_SetItemString(PyModule_GetDict(module), tmp, dict);
1101 snprintf(tmp,
sizeof(tmp),
"Crossfire_%s",
name);
1103 cst = PyModule_New(tmp);
1105 while (constants[i].
name != NULL) {
1106 PyModule_AddIntConstant(cst, (
char *)constants[i].
name, constants[i].value);
1109 PyDict_SetItemString(PyModule_GetDict(module),
name, cst);
1435 PyObject *
d = PyModule_GetDict(
m);
1464 PyModule_AddObject(
m,
"LogError", Py_BuildValue(
"i",
llevError));
1465 PyModule_AddObject(
m,
"LogInfo", Py_BuildValue(
"i",
llevInfo));
1466 PyModule_AddObject(
m,
"LogDebug", Py_BuildValue(
"i",
llevDebug));
1467 PyModule_AddObject(
m,
"LogMonster", Py_BuildValue(
"i",
llevMonster));
1469 CFPythonError = PyErr_NewException(
"Crossfire.error", NULL, NULL);
1476 PyModuleDef_HEAD_INIT,
1498 const char *stdOutErr =
1500 class CatchOutErr:\n\
1501 def __init__(self):\n\
1503 def write(self, txt):\n\
1504 self.value += txt\n\
1505 catchOutErr = CatchOutErr()\n\
1506 sys.stdout = catchOutErr\n\
1507 sys.stderr = catchOutErr\n\
1523 m = PyImport_ImportModule(
"Crossfire");
1533 m = PyImport_AddModule(
"__main__");
1534 PyRun_SimpleString(stdOutErr);
1535 catcher = PyObject_GetAttrString(
m,
"catchOutErr");
1541 const char *propname;
1545 va_start(args,
type);
1546 propname = va_arg(args,
const char *);
1547 if (!strcmp(propname,
"Identification")) {
1548 buf = va_arg(args,
char *);
1549 size = va_arg(args,
int);
1553 }
else if (!strcmp(propname,
"FullName")) {
1554 buf = va_arg(args,
char *);
1555 size = va_arg(args,
int);
1617 for (
int e = 0; eventFiles[e] != NULL; e++) {
1618 free(eventFiles[e]);
1629 char **eventFiles = NULL;
1632 int allocated = 0, current = 0;
1642 eventFiles =
static_cast<char **
>(calloc(1,
sizeof(eventFiles[0])));
1643 eventFiles[0] = NULL;
1647 while ((
d =
readdir(dp)) != NULL) {
1650 if (S_ISDIR(sb.st_mode)) {
1653 if (strcmp(
d->d_name + strlen(
d->d_name) - 3,
".py")) {
1657 if (allocated == current) {
1659 eventFiles =
static_cast<char **
>(realloc(eventFiles,
sizeof(
char *) * (allocated + 1)));
1660 for (
int i = current; i < allocated + 1; i++) {
1661 eventFiles[i] = NULL;
1664 eventFiles[current] = strdup(
name);
1672 PyObject *scriptfile;
1678 for (i = 0;
GECodes[i] != 0; i++)
1682 if (scriptfile != NULL) {
1685 Py_DECREF(scriptfile);
1699 for (
int i = 0;
GECodes[i] != 0; i++) {
1716 va_start(args,
type);
1728 op = va_arg(args,
object *);
1733 op = va_arg(args,
object *);
1735 op = va_arg(args,
object *);
1741 op = va_arg(args,
object *);
1742 object* hitter = va_arg(args,
object *);
1749 pl = va_arg(args,
player *);
1751 buf = va_arg(args,
char *);
1757 pl = va_arg(args,
player *);
1759 buf = va_arg(args,
char *);
1765 op = va_arg(args,
object *);
1770 op = va_arg(args,
object *);
1772 buf = va_arg(args,
char *);
1778 op = va_arg(args,
object *);
1780 buf = va_arg(args,
char *);
1786 op = va_arg(args,
object *);
1788 buf = va_arg(args,
char *);
1794 op = va_arg(args,
object *);
1800 op = va_arg(args,
object *);
1813 op = va_arg(args,
object *);
1814 buf = va_arg(args,
char *);
1818 op = va_arg(args,
object *);
1849 for (
int file = 0;
files[file] != NULL; file++)
1852 (*copy) = (*context);
1854 Py_XINCREF(copy->
event);
1855 Py_XINCREF(copy->
third);
1856 Py_XINCREF(copy->
who);
1892 va_start(args,
type);
1897 buf = va_arg(args,
char *);
1901 event = va_arg(args,
object *);
#define EVENT_GSOLD
Player sold object in shop, but global.
void cf_cost_string_from_value(uint64_t cost, int largest_coin, char *buffer, int length)
Wrapper for cost_string_from_value modified to take a char* and length instead of a StringBuffer.
@ CLASS
Object for applying character class modifications to someone.
static PyObject * findPlayer(PyObject *self, PyObject *args)
static PyObject * getMonthName(PyObject *self, PyObject *args)
void cf_log(LogLevel logLevel, const char *format,...)
Wrapper for LOG().
const CFConstant cstMove[]
time_t used_time
Last use of this cache entry.
#define ATNR_CANCELLATION
static PyObject * getCFPythonVersion(PyObject *self, PyObject *args)
static PyObject * shared_data
const char * cf_get_weekday_name(int index)
PyObject_HEAD object * obj
sstring cf_add_string(const char *str)
Wrapper for add_string().
sstring replies[MAX_REPLIES]
Description for replies_words.
static PyObject * getSeasonName(PyObject *self, PyObject *args)
const char * cf_get_directory(int id)
Gets a directory Crossfire uses.
static PyObject * getLocalDirectory(PyObject *self, PyObject *args)
@ MONSTER
A real, living creature.
@ CLOSE_CON
Eneq((at)csd.uu.se): Id for close_container archetype.
#define WAITATT
Wait for player to approach then hit, move if hit.
@ llevError
Error, serious thing.
@ EVENT_CONNECTOR
Lauwenmark: an invisible object holding a plugin event hook.
#define MOVE_ALL
Mask of all movement types.
const char * cf_get_season_name(int index)
#define ALLRUN
Always run, never attack good for sim.
sstring replies_words[MAX_REPLIES]
Available reply words.
int npc_msg_count
How many NPCs reacted to the text being said.
@ FLESH
animal 'body parts' -b.t.
static PyCodeObject * compilePython(char *filename)
Outputs the compiled bytecode for a given python file, using in-memory caching of bytecode.
#define RUNATT
Run but attack if player catches up to object.
#define PACEV2
The monster will pace as above but the length of the pace area is longer and the monster stops before...
#define NDI_GREEN
SeaGreen.
#define EVENT_GBOUGHT
Player bought object in shop, but global.
static event_registration c
CF_PLUGIN int postInitPlugin(void)
Plugin was initialized, now to finish.
CF_PLUGIN int eventListener(int *type,...)
Handles an object-related event.
object * cf_create_object_by_name(const char *name)
Wrapper for create_archetype() and create_archetype_by_object_name().
#define HITRUN
Run to then hit player then run away cyclicly.
static PyObject * setPlayerMessage(PyObject *self, PyObject *args)
static PyObject * CFPythonError
object * ob
The object representing the player.
static CFPContext * popContext(void)
Represents the ingame time.
#define EVENT_TIMER
Timer connected triggered it.
static PyObject * getPrivateDictionary(PyObject *self, PyObject *args)
int cf_timer_destroy(int id)
Destroys specified timer, equivalent of calling cftimer_destroy().
non standard information is not specified or uptime this means how long since the executable has been started A particular host may have been running a server for quite a long time
#define CFAPI_SYSTEM_ARCHETYPES
static PyObject * getConfigDirectory(PyObject *self, PyObject *args)
int initPlugin(const char *iversion, f_plug_api gethooksptr)
Plugin initialisation function.
#define EVENT_MAPLOAD
A map is loaded (pristine state)
#define NDI_ALL_DMS
Inform all logged in DMs.
PyObject * Crossfire_Party_wrap(partylist *what)
@ SKILL
Also see SKILL_TOOL (74) below.
static const flag_definition flags[]
Flag mapping.
static PyObject * createCFObject(PyObject *self, PyObject *args)
static PyObject * registerCommand(PyObject *self, PyObject *args)
static pycode_cache_entry pycode_cache[PYTHON_CACHE_SIZE]
Cached compiled scripts.
#define EVENT_LOGOUT
Player logout.
#define RANDO
The monster will go in a random direction until it is stopped by an obstacle, then it chooses another...
static void pushContext(CFPContext *context)
#define COMMAND_TYPE_NORMAL
Standard commands.
@ llevMonster
Many many details.
#define EVENT_SAY
Someone speaks.
@ TRANSPORT
see doc/Developers/objects
@ POTION_RESIST_EFFECT
A force, holding the effect of a resistance potion.
@ FLOOR
Floor tile -> native layer 0.
#define CFAPI_SYSTEM_PLAYERS
#define NAME_MAX
NAME_MAX used by random maps may not be defined on pure ansi systems.
static char ** getEventFiles(CFPContext *context)
Get the list of script files to run for the specified global event context.
static std::vector< std::pair< object *, tag_t > > friends
List of all friendly objects, object and its count.
void Handle_Map_Unload_Hook(Crossfire_Map *map)
#define NDI_BLUE
Actually, it is Dodger Blue.
CFPContext * current_context
#define HUGE_BUF
Used for messages - some can be quite long.
#define PACEH
The monster will pace back and forth until attacked.
PyObject * Crossfire_Object_wrap(object *what)
Python initialized.
const CFConstant cstMessageFlag[]
Plugin animator file specs[Config] name
sstring message
If not NULL, what the player will be displayed as said.
int replies_count
How many items in replies_words and replies.
void cf_system_get_region_vector(int property, std::vector< region * > *list)
static PyObject * getDataDirectory(PyObject *self, PyObject *args)
@ TRIGGER_MARKER
inserts an invisible, weightless force into a player with a specified string WHEN TRIGGERED.
PyTypeObject Crossfire_PlayerType
static event_registration m
CF_PLUGIN void * getPluginProperty(int *type,...)
Gets a plugin property.
time_t cached_time
Time this cache entry was created.
DIR * opendir(const char *)
#define EVENT_LOGIN
Player login.
the server will also quite happily load unpacked files as long as they have the right file which is convenient if you want to edit your maps and archetypes live It also contains a few files
static PyObject * setReturnValue(PyObject *self, PyObject *args)
void cf_log_plain(LogLevel logLevel, const char *message)
Wrapper for LOG() that uses directly a buffer, without format.
#define EVENT_STOP
Thrown object stopped.
#define EVENT_CLOCK
Global time event.
static PyObject * getArchetypes(PyObject *self, PyObject *args)
@ MARKER
inserts an invisible, weightless force into a player with a specified string.
static PyObject * getMapHasBeenLoaded(PyObject *self, PyObject *args)
static const char * GEPaths[]
#define EVENT_PICKUP
Object picked up.
#define EVENT_CRASH
Triggered when the server crashes.
static PyObject * getMapDirectory(PyObject *self, PyObject *args)
#define EVENT_SELLING
Object is being sold by another one.
#define MOVE_WALK
Object walks.
int cf_find_face(const char *name, int error)
Wrapper for find_face().
@ BUILDER
Generic item builder, see subtypes below.
#define EVENT_DROP
Object dropped on the floor.
static PyObject * findFace(PyObject *self, PyObject *args)
#define EVENT_TRIGGER
Button pushed, lever pulled, etc.
void(* f_plug_api)(int *type,...)
General API function.
#define EVENT_MAPENTER
A player entered a map.
const CFConstant cstDirection[]
static PyObject * npcSay(PyObject *self, PyObject *args)
void cf_system_unregister_global_event(int event, const char *name)
const char * cf_get_month_name(int index)
int cf_init_plugin(f_plug_api getHooks)
@ INORGANIC
metals, minerals, dragon scales
struct dirent * readdir(DIR *)
mapstruct * cf_get_empty_map(int sizex, int sizey)
Wrapper for get_empty_map().
static PyObject * unregisterGEvent(PyObject *self, PyObject *args)
#define EVENT_MAPRESET
A map is resetting.
PyCodeObject * code
Compiled code, NULL if there was an error.
static void addSimpleConstants(PyObject *module, const char *name, const CFConstant *constants)
Do half the job of addConstants.
#define MOVE_FLYING
Combo of fly_low and fly_high.
static PyObject * costStringFromValue(PyObject *self, PyObject *args)
@ PEACEMAKER
Object owned by a player which can convert a monster into a peaceful being incapable of attack.
#define MAX_NPC
How many NPCs maximum will reply to the player.
#define CIRCLE2
Same as CIRCLE1 but a larger circle is used.
CF_PLUGIN char SvnRevPlugin[]
static int do_script(CFPContext *context)
static PyObject * getSharedDictionary(PyObject *self, PyObject *args)
#define EVENT_BORN
A new character has been created.
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your message
#define EVENT_MAPUNLOAD
A map is freed (includes swapping out)
@ SKILLSCROLL
can add a skill to player's inventory -bt.
static PyObject * createMap(PyObject *self, PyObject *args)
static void set_exception(const char *fmt,...)
Set up an Python exception object.
static PyObject * catcher
A Python object receiving the contents of Python's stderr, and used to output to the Crossfire log in...
PyObject * Crossfire_Archetype_wrap(archetype *what)
@ DRAGON_FOCUS
Used during character creation.
static PyObject * getFriendlyList(PyObject *self, PyObject *args)
static command_registration registered_commands[MAX_COMMANDS]
#define ATNR_COUNTERSPELL
CF_PLUGIN int closePlugin(void)
Close the plugin.
static PyObject * getPlayers(PyObject *self, PyObject *args)
#define EVENT_USER
User-defined event.
#define CFAPI_SYSTEM_MAPS
#define EVENT_THROW
Object is thrown.
@ rt_reply
Reply to something.
#define PACEV
The monster will pace back and forth until attacked.
PyObject * Crossfire_Region_wrap(region *what)
#define CFAPI_SYSTEM_REGIONS
#define EVENT_SHOUT
A player 'shout' something.
static PyObject * getUniqueDirectory(PyObject *self, PyObject *args)
static PyObject * matchString(PyObject *self, PyObject *args)
void cf_free_string(sstring str)
Wrapper for free_string().
const CFConstant cstTime[]
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for d
#define EVENT_BOUGHT
Object is being bought by player.
#define COMMAND_TYPE_WIZARD
Wizard-only commands.
pluglist shows those as well as a short text describing each the list will simply appear empty The keyword for the Python plugin is Python plugout< keyword > Unloads a given identified by its _keyword_ So if you want to unload the Python you need to do plugout Python plugin< libname > Loads a given whose _filename_ is libname So in the case of you d have to do a plugin cfpython so Note that all filenames are relative to the default plugin path(SHARE/plugins). Console messages. ----------------- When Crossfire starts
static PyObject * destroyTimer(PyObject *self, PyObject *args)
#define MOVE_FLY_LOW
Low flying object.
#define EVENT_PLAYER_DEATH
Global Death event
static FILE * cfpython_pyfile_asfile(PyObject *obj)
Return a file object from a Python file (as needed for compilePython() and postInitPlugin())
const CFConstant cstType[]
command_registration cf_system_register_command_extra(const char *name, const char *extra, command_function_extra func, uint8_t command_type, float time)
static PyObject * getPlayerDirectory(PyObject *self, PyObject *args)
static PyObject * getWeekdayName(PyObject *self, PyObject *args)
static PyObject * getMaps(PyObject *self, PyObject *args)
const CFConstant cstReplyTypes[]
void cf_system_get_object_vector(int property, std::vector< object * > *list)
void replace(const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
Replace in string src all occurrences of key by replacement.
reply_type
Various kind of messages a player or NPC can say.
void cf_system_register_global_event(int event, const char *name, f_plug_event hook)
reply_type message_type
A reply_type value for message.
static PyObject * cfpython_openpyfile(char *filename)
Open a file in the way we need it for compilePython() and postInitPlugin().
static PyObject * getTempDirectory(PyObject *self, PyObject *args)
sstring file
Script full path.
#define NDI_UNIQUE
Print immediately, don't buffer.
#define EVENT_MUZZLE
A player was Muzzled (no_shout set).
int cf_find_animation(const char *txt)
Wrapper for find_animation().
this information may not reflect the current implementation This brief document is meant to describe the operation of the crossfire as well as the form of the data The metaserver listens on port for tcp and on port for udp packets The server sends updates to the metaserver via udp The metaserver only does basic checking on the data that server sends It trusts the server for the ip name it provides The metaserver does add the ip address and also tracks the idle time(time since last packet received). The client gets its information from the metaserver through connecting by means of tcp. The client should retrieve http the body s content type is text plain The current metaserver implementation is in Perl But the metaserver could be in any language perl is fast enough for the amount of data that is being exchanged The response includes zero or more server entries Each entry begins with the line START_SERVER_DATA and ends with the line END_SERVER_DATA Between these lines key value pairs("key=value") may be present. The entries are sent in arbitrary order. A client should apply some ordering when displaying the entries to the user. TODO b additional information outside BEGIN_SERVER_DATA END_SERVER_DATA maps
sstring slaying
Which race to do double damage to.
static PyObject * getReturnValue(PyObject *self, PyObject *args)
PyMethodDef CFPythonMethods[]
#define EVENT_TELL
A player 'tell' something.
#define EVENT_DEATH
Player or monster dead.
sstring name
The name of the object, obviously...
@ BATTLEGROUND
battleground, by Andreas Vogl
PyTypeObject Crossfire_PartyType
#define CFAPI_SYSTEM_FRIENDLY_LIST
#define MAX_REPLIES
How many NPC replies maximum to tell the player.
std::vector< archetype * > players
const char * cf_get_periodofday_name(int index)
static PyObject * getWhatIsMessage(PyObject *self, PyObject *args)
static PyObject * getScriptName(PyObject *self, PyObject *args)
static PyModuleDef CrossfireModule
static void python_command_function(object *op, const char *params, const char *script)
@ DUPLICATOR
Duplicator/multiplier object.
static const char * getGlobalEventPath(int code)
void cf_system_get_map_vector(int property, std::vector< mapstruct * > *list)
static PyObject * createCFObjectByName(PyObject *self, PyObject *args)
const CFConstant cstAttackType[]
const typedef char * sstring
@ rt_question
Asking a question.
#define NDI_ALL
Inform all players of this message.
const CFConstant cstAttackTypeNumber[]
PyTypeObject Crossfire_ArchetypeType
mapstruct * cf_map_has_been_loaded(const char *name)
Wrapper for has_been_loaded().
@ MATERIAL
Material for building.
#define EVENT_TIME
Triggered each time the object can react/move.
const char * cf_re_cmp(const char *str, const char *regexp)
Wrapper for re_cmp().
#define EVENT_MAPLEAVE
A player left a map.
static PyObject * getRegions(PyObject *self, PyObject *args)
static PyObject * readyMap(PyObject *self, PyObject *args)
@ SKILL_TOOL
Allows the use of a skill.
static PyObject * getParties(PyObject *self, PyObject *args)
@ SHOP_INVENTORY
Mark Wedel (mark@pyramid.com) Shop inventories.
object * cf_create_object(void)
Wrapper for object_new().
char * cf_get_maps_directory(const char *name, char *buf, int size)
Wrapper for create_pathname().
Structure used to build up dialog information when a player says something.
#define EVENT_REMOVE
A Player character has been removed.
static void freeEventFiles(char **eventFiles)
Clear the list of event files.
static void cfpython_init_types(PyObject *m)
Set up the various types (map, object, archetype and so on) as well as some constants,...
static std::unordered_map< std::string, Region * > regions
All defined regions.
#define NDI_DK_ORANGE
DarkOrange2.
static PyObject * addReply(PyObject *self, PyObject *args)
One compiled script, cached in memory.
static void initConstants(PyObject *module)
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - The server only reads data from the socket if the player has an action. This isn 't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn 't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object(starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::string, that is purely for informative that general client/server info(ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using.=====Version ID If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. The version are currently integers, in the form ABCD. A=1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands(either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won 't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it.=====Handling As far as the client is concerned, its _scval_ must be at least equal to the server, and its _csval_ should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won 't crash with a version mismatch(however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don 't match. NOTE:Since all packets have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE:For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn 't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|bot(0/1 value)|If set to 1, the client will not be considered a player when updating information to the metaserver. This is to avoid having a server with many bots appear more crowded than others.|darkness(0/1 value)|If set to 1(default), the server will send darkness information in the map protocol commands. If 0, the server will not include darkness, thus saving a minor amount of bandwidth. Since the client is free to ignore the darkness information, this does not allow the client to cheat. In the case of the old 'map' protocol command, turning darkness off will result in the masking faces not getting sent to the client.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|facecache(0/1)|Determines if the client is caching images(1) or wants the images sent to it without caching them(0). Default is 0. This replaces the setfacemode command.|faceset(8 bit)|Faceset the client wishes to use. If the faceset is not valid, the server returns the faceset the client will be using(default 0).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int y)|Sets the map size to x X y. Note the spaces here are only for clarity - there should be no spaces when actually sent(it should be 11x11 or 25x25). The default map size unless changed is 11x11. The minimum map size the server will allow is 9x9(no technical reason this could be smaller, but I don 't think the game would be smaller). The maximum map size supported in the current protocol is 63x63. However, each server can have its maximum map size sent to most any value. If the client sends an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 setup command. When the values are valid, the server will send back a mapsize XxY setup command. Note that this is from its parsed values, so it may not match stringwise with what the client sent, but will match 0 wise. For example, the client may send a 'mapsize 025X025' command, in which case the server will respond with a 'mapsize 25x25' command - the data is functionally the same. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|num_look_objects(int value)|The maximum number of objects shown in the ground view. If more objects are present, fake objects are created for selecting the previous/next group of items. Defaults to 50 if not set. The server may adjust the given value to a suitable one data
static PyObject * getWhoIsThird(PyObject *self, PyObject *args)
static PyObject * getScriptParameters(PyObject *self, PyObject *args)
sstring npc_msgs[MAX_NPC]
What the NPCs will say.
PyTypeObject Crossfire_RegionType
void cf_system_get_archetype_vector(int property, std::vector< archetype * > *list)
player * cf_player_find(const char *plname)
Wrapper for find_player_partial_name().
void cf_system_unregister_command(command_registration command)
#define PYTHON_CACHE_SIZE
Number of python scripts to store the bytecode of at a time.
#define CFAPI_SYSTEM_PARTIES
#define MOVE_BOAT
Boats/sailing.
#define MOVE_FLY_HIGH
High flying object.
CFPContext * context_stack
#define EVENT_ATTACKED
Object attacked, with weapon or spell.
#define EVENT_MAPREADY
A map is ready, either first load or after reload.
#define EVENT_CLOSE
Container closed.
uint64_t command_registration
Identifier when registering a command.
mapstruct * cf_map_get_map(const char *name, int flags)
Wrapper for ready_map_name().
#define WAIT2
Monster does not try to move towards player if far.
Crossfire Architecture the general intention is to enhance the enjoyability and playability of CF In this code
void cf_system_get_party_vector(int property, std::vector< partylist * > *list)
#define DISTHIT
Attack from a distance if hit as recommended by Frank.
#define RUSH
Rush toward player blindly, similiar to dumb monster.
#define PETMOVE
If the upper four bits of attack_movement are set to this number, the monster follows a player until ...
static PyObject * registerGEvent(PyObject *self, PyObject *args)
const CFConstant cstAttackMovement[]
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for but you habe then to change the pathes in the VC settings Go in Settings C and Settings Link and change the optional include and libs path to the new python installation path o except the maps ! You must download a map package and install them the share folder Its must look like doubleclick on crossfire32 dsw There are projects in your libcross lib and plugin_python You need to compile all Easiest way is to select the plugin_python ReleaseLog as active this will compile all others too Then in Visual C press< F7 > to compile If you don t have an appropriate compiler you can try to get the the VC copies the crossfire32 exe in the crossfire folder and the plugin_python dll in the crossfire share plugins folder we will remove it when we get time for it o Last showing lots of weird write to the Crossfire mailing list
LogLevel
Log levels for the LOG() function.
#define MOVE_SWIM
Swimming object.
#define DISTATT
Move toward a player if far, but maintain some space, attack from a distance - good for missile users...
static PyObject * log_message(PyObject *self, PyObject *args)
#define EVENT_ATTACKS
Weapon or arrow hitting something.
#define EVENT_DESTROY
Object destroyed (includes map reset/swapout)
static PyObject * getWhoAmI(PyObject *self, PyObject *args)
static void log_python_error(void)
Trace a Python error to the Crossfire log.
static PyObject * getTime(PyObject *self, PyObject *args)
#define EVENT_GKILL
Triggered when anything got killed by anyone.
static void addConstants(PyObject *module, const char *name, const CFConstant *constants)
Add constants and a reverse dictionary to get the name from the value.
#define CIRCLE1
If the upper four bits of move_type / attack_movement are set to this number, the monster will move i...
PyObject * Crossfire_Map_wrap(mapstruct *what)
static PyObject * private_data
#define ATNR_LIFE_STEALING
#define PACEH2
The monster will pace as above but the length of the pace area is longer and the monster stops before...
PyTypeObject Crossfire_ObjectType
static PyObject * getPeriodofdayName(PyObject *self, PyObject *args)
#define EVENT_KICK
A player was Kicked by a DM
const CFConstant cstEventType[]
PyObject * PyInit_cjson(void)
CF_PLUGIN int cfpython_globalEventListener(int *type,...)
static PyObject * getWhoIsActivator(PyObject *self, PyObject *args)
@ DETECTOR
peterm: detector is an object which notices the presense of another object and is triggered like butt...
#define NDI_LT_GREEN
DarkSeaGreen, which is actually paler than seagreen - also background color.
static PyObject * PyInit_Crossfire(void)
static void initContextStack(void)
void cf_get_time(timeofday_t *tod)
@ llevDebug
Only for debugging purposes.
static PyObject * findAnimation(PyObject *self, PyObject *args)
#define RANDO2
Constantly move in a different random direction.
#define EVENT_APPLY
Object applied-unapplied.
static PyObject * getEvent(PyObject *self, PyObject *args)
static void freeContext(CFPContext *context)
PyTypeObject Crossfire_MapType