 |
Crossfire Server, Trunk
1.75.0
|
Go to the documentation of this file.
41 #define TAG_START "[color=#aa55ff]"
42 #define TAG_END "[/color]"
45 #define QC_CAN_RESTART -1
74 for (
const auto qs : quest->
steps) {
102 int warned = 0, state;
107 file = fopen(
final,
"r");
113 while (fgets(read,
sizeof(read), file) != NULL) {
114 if (sscanf(read,
"quest %s\n",
data)) {
123 LOG(
llevError,
"quest: invalid file format for %s\n",
final);
128 if (sscanf(read,
"state %d\n", &state)) {
130 if (quest != NULL && state != -1) {
135 else if (
step->is_completion_step)
140 if (strcmp(read,
"end_quest\n") == 0) {
155 if (sscanf(read,
"completed %d\n", &state)) {
160 LOG(
llevError,
"quest: invalid line in %s: %s\n",
final, read);
164 LOG(
llevError,
"quest: missing end_quest in %s\n",
final);
190 fprintf(file,
"quest %s\n", state->
code);
191 fprintf(file,
"state %d\n", state->
state);
193 fprintf(file,
"end_quest\n");
300 if (conditions.empty())
302 for (
const auto cond : conditions) {
304 if (cond->minstep < 0 && cond->maxstep < 0) {
309 if (current_step < cond->minstep || current_step > cond->maxstep)
321 new_step=new_step<
step->step?
step->step:new_step;
326 if (new_step > current_step) {
358 LOG(
llevError,
"quest: asking for set_state of unknown quest %s!\n", quest_code);
363 if (!dm && state <= 0) {
364 LOG(
llevDebug,
"quest_set_player_state: warning: called with invalid state %d for quest %s, player %s\n", state, pl->
ob->
name, quest_code);
368 if (started && qs->
state == 0) {
370 LOG(
llevDebug,
"quest_set_player_state: warning: called for player %s not having started quest %s\n", pl->
ob->
name, quest_code);
385 LOG(
llevError,
"quest_set_player_state: couldn't find state definition %d for quest %s, player %s\n", state, quest_code, pl->
ob->
name);
390 if (
step->is_completion_step) {
425 assert(
step != NULL);
427 strlen(
step->step_description));
450 int completed_count = 0, restart_count = 0, total_count = 0, current_count = 0;
455 if (quest->
parent == NULL) {
467 if (completed_count > 0) {
469 if (restart_count > 0)
471 "%s completed %d out of %zu quests, of which %d may be restarted.",
name, completed_count,
quests_count(
false), restart_count);
474 "%s completed %d quests",
name, completed_count);
475 current_count = completed_count;
478 "%s completed the following quests:",
name);
482 if (quest->
parent == NULL) {
487 "(%3d) %s%s", ++current_count, quest->
quest_title, restart);
494 if (total_count > completed_count) {
496 "%s started the following quests:",
name);
500 if (quest->
parent == NULL) {
544 if (number <= 0 || !pq) {
552 if (++questnum == number)
return state;
560 if (++questnum == number)
return state;
594 std::for_each(quest->
steps.cbegin(), quest->
steps.cend(), [&pl] (
const auto &
step) {
595 draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_QUESTS,
" " TAG_START
"Step:" TAG_END
" %d (%s)", step->step, step->step_description);
602 const char *restart =
"";
604 restart =
i18n(pl->
ob,
" (can be replayed)");
620 prefix =
"Current Status";
628 if (child->
parent == quest)
687 LOG(
llevError,
"quest_start: requested unknown quest %s\n", quest_code);
695 LOG(
llevDebug,
"quest_start: negative state %d for %s quest %s\n", state, pl->
ob->
name, quest_code);
700 LOG(
llevDebug,
"quest_start: warning: player %s has already started quest %s\n", pl->
ob->
name, quest_code);
744 LOG(
llevError,
"command_quest called for a non player!\n");
748 if (!params || *params ==
'\0') {
754 char* dup = strdup(params);
755 char* space = strchr(dup,
' ');
763 params = params + (space - dup) + 1;
779 if (strcmp(params,
"list all") == 0) {
784 if (strcmp(params,
"list") == 0) {
789 if (strncmp(params,
"info ", 5) == 0) {
790 int number = atoi(params+5);
799 if (strncmp(params,
"info_c ", 7) == 0) {
800 int number = atoi(params+7);
819 char *dup = strdup(params + 4);
820 char *space = strrchr(dup,
' ');
836 state = atoi(space + 1);
858 if (
d->parent != quest->
parent)
863 for (
int i = 0; i <
d->level; i++) {
864 strncat(prefix,
"-",
MAX_BUF - 1);
924 for (state = states->
quests; state != NULL; state = state->
next) {
927 if (state->
state == -1)
932 size = 2 + (2 + strlen(quest->
quest_title)) + 4 + 1 + (2 + (
step != NULL ? strlen(
step->step_description) : 0));
969 if (qp != NULL && qp->
quests != NULL) {
void SockList_AddInt(SockList *sl, uint32_t data)
Adds a 32 bit value.
#define NS_FACESENT_FACE
Bitmask for the faces_sent[] array - what portion of the face have we sent?
struct Settings settings
Server settings.
static void output_quests(const quest_definition *quest, void *user)
Dump one quest on the logfile, then its children.
#define MSG_TYPE_COMMAND_SUCCESS
Successful result from command.
@ llevError
Error, serious thing.
void quest_for_each(quest_op op, void *user)
Iterate over all quests.
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
int of_close(OutputFile *of)
Closes an output file.
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
static quest_player * get_or_create_quest(player *pl)
Get quest status for a player, creating it if it doesn't exist yet.
#define QUERY_FLAG(xyz, p)
static void quest_write_player_data(const quest_player *pq)
Write quest-data information for a player.
static quest_state * get_new_quest_state(void)
Return a new quest_state*, calling fatal() if memory shortage.
void quest_first_player_save(player *pl)
Ensure the quest state is correctly saved for a player.
static int quests_count
Count of quests.
void SockList_AddString(SockList *sl, const char *data)
Adds a string without length.
static void quest_set_state(player *dm, player *pl, sstring quest_code, int state, int started)
Set the state of a quest for a player.
object * ob
The object representing the player.
sstring quest_description
Quest longer description.
static void quest_info(player *pl, player *who, quest_state *qs, int level)
Give details about a quest.
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
#define NDI_ALL_DMS
Inform all logged in DMs.
#define MSG_TYPE_COMMAND_QUESTS
Quest info.
static quest_player * get_quest(player *pl)
Get quest status for a player, not creating it if it doesn't exist.
int was_completed
Whether the quest was completed once or not, indepandently of the state.
const quest_definition * parent
Parent to dump from.
static quest_state * get_quest_by_number(player *pl, int number)
returns the quest state which corresponds to a certain number for the given player.
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Adds a data block prepended with an 16 bit length field.
void SockList_Reset(SockList *sl)
Resets the length of the stored data for writing.
static void free_state(quest_player *pq)
Free quests structures.
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Plugin animator file specs[Config] name
int sent_to_client
Whether this state was sent to the client or not.
size_t SockList_Avail(const SockList *sl)
Returns the available bytes in a SockList instance.
sstring quest_title
Quest title for player.
#define MSG_TYPE_ADMIN_DM
DM related admin actions.
struct player * contr
Pointer to the player which control this object.
sstring add_refcount(sstring str)
This will increase the refcount of the string str.
void esrv_send_face(socket_struct *ns, const Face *face, int nocache)
Sends a face to a client if they are in pixmap mode, nothing gets sent in bitmap mode.
const Face * face
Face associated with this quest.
uint16_t number
This is the image unique identifier.
static void do_update(const quest_definition *quest, void *user)
sstring add_string(const char *str)
This will add 'str' to the hash table.
sstring quest_code
Quest internal code.
void SockList_AddShort(SockList *sl, uint16_t data)
Adds a 16 bit value.
#define NDI_DELAYED
If set, then message is sent only after the player's tick completes.
void SockList_AddChar(SockList *sl, unsigned char c)
Adds an 8 bit value.
void dump_quests(void)
Dump all of the quests, then calls exit() - useful in terms of debugging to make sure that quests are...
Structure used when dumping quests to stdout.
static quest_step_definition * quest_get_step(quest_definition *quest, int step)
Get a step for the specified quest.
struct quest_definition * parent
Parent for this quest, NULL if it is a 'top-level' quest.
static void update_quests(player *pl)
Look through all of the quests for the given player, and see if any need to be updated.
static void quest_list(player *pl, player *who, int showall, const char *name)
Display current and completed player quests.
void command_help(object *op, const char *params)
Player is asking for some help.
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 step
int is_complete
Whether the quest is complete in the current playthrough.
FILE * logfile
Used by server/daemon.c.
quest_state * quests
Quests done or in progress.
quest_definition * quest_get_by_code(sstring code)
Find a quest from its code if it exists.
void SockList_Init(SockList *sl)
Initializes the SockList instance.
static void quest_display(player *pl, quest_player *pq, int showall, const char *name)
Utility function to display a quest list.
static quest_state * get_or_create_state(quest_player *pq, sstring name)
Get the state of a quest for a player, creating it if not existing yet.
uint32_t client_code
The code used to communicate with the client, merely a unique index.
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
void quest_set_player_state(player *pl, sstring quest_code, int state)
Set the state of a quest for a player.
#define MAX_BUF
Used for all kinds of things.
#define MSG_TYPE_ADMIN_LOADSAVE
load/save operations
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
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
void quest_send_initial_states(player *pl)
Send the current quest states for the specified player, if the client supports those notifications.
void SockList_Term(SockList *sl)
Frees all resources allocated by a SockList instance.
const char * playerdir
Where the player files are.
int quest_get_player_state(player *pl, sstring quest_code)
Get the quest state for a player.
#define MSG_TYPE_COMMAND_FAILURE
Failed result from command.
quest_definition * quest_find_by_code(sstring code)
Find a quest from its code, logging if no matching quest.
#define FLAG_WIZ
Object has special privilegies.
#define NDI_UNIQUE
Print immediately, don't buffer.
sstring name
The name of the object, obviously...
void quest_start(player *pl, sstring quest_code, int state)
Start a quest for a player.
Information about a quest for a player.
#define QC_CAN_RESTART
Quest status that indicates a quest was completed and may be restarted.
static void quest_read_player_data(quest_player *pq)
Read quest-data information for a player.
const typedef char * sstring
sstring player_name
Player's name.
Definition of an in-game quest.
sstring code
Quest internal code.
static quest_state * get_state(quest_player *pq, sstring name)
Get the state of a quest for a player, not creating if not existing yet.
quest_player * next
Next player on the list.
int quest_was_completed(player *pl, sstring quest_code)
Check if a quest was completed once for a player, without taking account the current state.
int state
State for the player.
player * find_player_partial_name(const char *plname)
Find a player by a partial name.
uint8_t * faces_sent
This is a bitmap on sent face status.
====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
Information about a player.
void command_quest(object *op, const char *params)
Command handler for 'quest'.
SockList * player_get_delayed_buffer(player *pl)
Get a delayed socket buffer, that will be sent after the player's tick is complete.
uint32_t has_directory
If 0, the player was not yet saved, its directory doesn't exist.
uint16_t notifications
Notifications this client wants to get.
socket_struct * socket
Socket information for this player.
int quest_restart
If non zero, can be restarted.
int level
Indentation level.
void Send_With_Handling(socket_struct *ns, SockList *sl)
Calls Write_To_Socket to send data to the client.
Contains the base information we use to make up a packet we want to send.
void free_quest(void)
Free all quest status structures.
quest_state * next
Next quest on the list.
static quest_player * player_states
Player quest state.
@ llevDebug
Only for debugging purposes.
static int evaluate_quest_conditions(const std::vector< quest_condition * > conditions, player *pl)
Checks whether the conditions for a given step are met.
const char * i18n(const object *who, const char *code)
Translate a message in the appropriate language.
std::vector< quest_step_definition * > steps
Quest steps.
const char * localdir
Read/write data files.