From 6f6529a4bbc4dceaaed7e9c56b482e83fb809de9 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Sat, 3 Jan 2015 12:11:33 -0800 Subject: Document every network packet's pre, post, and desc With pretty pictures (not a submodule because large images). Part of this commit was originally made by Rawng. --- Makefile.in | 13 + doc/mapserver-logging | 71 - doc/old-spell-language | 870 ------------ old-doc/mapserver-logging | 71 + old-doc/old-spell-language | 870 ++++++++++++ src/admin/ladmin.cpp | 370 ----- src/char/char.cpp | 86 -- src/login/login.cpp | 219 +-- src/map/battle.cpp | 11 - src/map/battle.hpp | 1 - src/map/chrif.cpp | 133 +- src/map/chrif.hpp | 1 - src/map/clif.cpp | 108 +- src/map/clif.hpp | 5 - src/map/npc.cpp | 21 - src/map/npc.hpp | 1 - src/map/pc.cpp | 180 +-- src/map/pc.hpp | 5 +- src/mmo/skill.t.hpp | 3 + tools/protocol.py | 3207 ++++++++++++++++++++++++++++++++++++++------ 20 files changed, 3784 insertions(+), 2462 deletions(-) delete mode 100644 doc/mapserver-logging delete mode 100644 doc/old-spell-language create mode 100644 old-doc/mapserver-logging create mode 100644 old-doc/old-spell-language diff --git a/Makefile.in b/Makefile.in index 0002153..7cf6080 100644 --- a/Makefile.in +++ b/Makefile.in @@ -224,6 +224,11 @@ TEST_BINARIES := $(patsubst src/%/test.cpp,bin/tests/test-%,${TEST_SOURCES}) GTEST_BINARIES := $(patsubst src--%_test.cpp,bin/tests/gtest-%,$(subst /,--,${GTEST_SOURCES})) DTEST_BINARIES := $(patsubst src/debug-debug/%.cpp,bin/tests/dtest-%,${DTEST_SOURCES}) +DOC_DOTS := $(shell cd ${SRC_DIR}; find doc-gen/ -name '*.gv') +DOC_PNGS := $(patsubst %.gv,${SRC_DIR}/%.png,${DOC_DOTS}) +DOC_OBSOLETE := +DOC_OBSOLETE += $(filter-out ${DOC_PNGS},$(wildcard ${SRC_DIR}/doc-gen/*.png)) + # tricky part # We can't put comments in a macro so here goes: @@ -747,6 +752,14 @@ stamp/%.tcc.formatted: src/%.tcc tools/indenter touch $@ .PHONY: format format-cpp format-hpp +vpath doc-gen/%.gv ${SRC_DIR} +${SRC_DIR}/doc-gen/%.png: doc-gen/%.gv + dot -Tpng $< -o $@ +doc: ${DOC_PNGS} + test -e ${SRC_DIR}/doc-gen/.git + rm -f ${DOC_OBSOLETE} +.PHONY: doc + most: $(filter-out bin/${tmwa}-map,${BINARIES}) magic: $(filter obj/map/magic%,${PDC_OBJECTS}) common: $(filter-out %/lib.pdc.o obj/debug-debug/% %_test.pdc.o obj/login/% obj/char/% obj/map/% obj/admin/% obj/monitor/%,${PDC_OBJECTS}) diff --git a/doc/mapserver-logging b/doc/mapserver-logging deleted file mode 100644 index 573c61f..0000000 --- a/doc/mapserver-logging +++ /dev/null @@ -1,71 +0,0 @@ -Log format: - -LOGLINE ::= int'.'int - -MESSAGE ::= 'log-start' - | 'log-start v2' - | 'log-start v3' - | 'log-start v4' - | - | - -COORD ::= int':'int','int - -PC ::= 'PC'int - -MOB ::= 'MOB'int - -TARGET ::= | - -SPELLRESULT ::= 'SUCCESS' | 'FAILURE' - -STATPLACE ::= 'LOGIN' | 'STATUP' | 'STATUP2' | 'STATRESET' - -XPPLACE ::= 'LOGIN' | 'LEVELUP' - -XPREASON ::= 'SCRIPTXP' | 'HEALXP' | 'KILLXP' | 'SHAREXP' | 'UNKNOWNXP' - -ZEROTARGET ::= 'null' | - -MOB-MESSAGE ::= 'DEAD' - -PC-MESSAGE ::= 'WPNDMG' int 'FOR' int WPN int # "WPNDMG MOB01 type FOR damage WPN weapon-item" - | 'WPNINJURY' int 'FOR' int - | 'MOB-TO-MOB-DMG' 'FROM' int 'TO' int 'FOR' int # Summoned monster damage - | 'SPELLHEAL-INSTA' FOR int - | 'SPELLDMG' FOR int BY spell-id - | 'CAST' spell-id - | 'STAT' int int int int int int # str agi vit int dex luk - | 'XP' int int 'JOB' int int int 'ZENY' int '+' int # level xp job-level skill-xp skill-pts zeny bank-zeny - | 'SKILLPOINTS-UP' int # new number of unused skill points - | 'MAGIC' int int int int int int 'EXP' int int 'LOGIN' # magic life war transmute nature ether magic-xp healer-xp - | 'SKILL-ACTIVATE' int int int # skillid skilllvl skillpower - | 'SKILL-UP' int int int # skillid skilllvl skillpower - | 'SKILL-DEACTIVATE' int - | 'MAGIC-ABSORB-DMG' int - | 'GAINXP' int - | 'PICKUP' int int # item-id count - | 'INJURED-BY' 'FOR' int - | 'DEAD' - | 'LOGOUT' - | 'TRADECOMMIT' 'WITH' int 'GIVE' int 'GET' int # pc-id give-zeny get-zeny - | 'TRADECANCEL' - | 'TRADEOK' - | 'STATUP' - | 'STATRESET' - | 'STATUP2' - -NOTES: ------- -1. Any PICKUP between TRADECOMMIT and TRADEOK is part of the trade. -2. TRADECOMMIT is issued for both participants in a trade. -3. INJURED-BY seems to only duplicate information we already have. - - -Illegal items - -645, 668, 659, 731, 734, 744, 745, 755, 756, 757, 747, 748, 721, 722 - -Restricted access - -725, 726, 647, 646 diff --git a/doc/old-spell-language b/doc/old-spell-language deleted file mode 100644 index 5f70f89..0000000 --- a/doc/old-spell-language +++ /dev/null @@ -1,870 +0,0 @@ --------------------------------------------------------------------------------- -== Spell language - -* Comments: One-line comments using '#' or '//' as prefix -* names vs. invocations: spells and anchors have both names - and invocations. Names are used to refer to spells and - anchors from within scripts without revealing their - invocations. - - -Each spell specification file consists of a sequence of definitions of -globals, anchors, spells, and procedures. - -Types: ------- - These are the primitive types: - * int - * string - * dir (a direction: N, S, SE, NW etc.) - * location (a single spot in the world) - * entity (a PC, NPC, or monster) - * area (a set of locations) - * spell (a spell) - * invocation (a spell instance) - * fail (a separate type that arises implicitly when a function - fails) - - `fail' arises in special circumstances in some functions, e.g. when -trying to divide by zero, or when function parameters fail to -type-check. When a fail value flows into an operation, than that -operation is skipped. When a fail value flows into a function, then -that function evaluates to fail, except for - - * if_then_else (which permits `fail' in its true or false branch) - * failed (which returns true iff the argument is of type fail). - - We will use standard functional type notation: - - `int * string -> entity' denotes the type of a function that accepts -an integer and a string and returns an entity. We use the return `()' -for operations (which return nothing). - -Globals: --------- - A `global' is a global variable, declared and defined in the -same line: - - foo = "this is a string"; - - Expressions can be arbitrary language expressions. Note that globals -can only reference the values of globals defined earlier in the same -program. - - There are two special globals: - - * min_casttime : int - Minimal number of milliseconds of cast delay, no matter what any - given spell may say. Cast delay is the time before the next spell - can be cast. - - * obscure : int - Chance of a character of a given spell to be obscured (masked out - by an asterisk). - - - Globals can also be defined as CONST (though this should not be done -for the special globals listed above or they will not take effect.) -CONST-defined globals cannot be re-defined. - - -Expressions: ------------- - Expressions occur in globals, anchors, spells, and procedures. -Expressions evaluate to values of a given type. Expressions can be -simple literals, area literals, infix expressions, function -applications, or variable refecences. - -- Simple literals: - * 42, 0xff: int literals - * N, S, E, W, NE, NW, SE, SW : dir literals - * "foo bar" : string literals - -- Area literals: - * @("new_3-1.gat", 26, 26) - This area denotes a single field on a map (new_3-1.gat, - co-ordinates (26,26). - * @("new_3-1.gat", 26, 26) @+ (10, 10) - This area is 100 fields, stretching to a field of size 10x10 to - the south and east of (26,26) in new_3-1.gat. - * @("new_3-1.gat", 26, 26) towards S (5, 3) - This area is a rectangular field of depth 3 and width 5 to the - south of (26,26) in new_3-1.gat. `depth 3' here means that it - stretches three fields to the south. `width 5' here means that it - extends five fields to the east and five to the west of the - specified coordinate, for a total width of 11. - The only directions supported here are S, E, N, W. - -- Infix expressions: - Infix expressions are special functions in which the operator is - written between two expressions. Infix expressions largely follow - the C precedence rules. The following infix operators are - supported: - * addition (+) (operates on ints, concatenates strings, and - constructs the union of areas) - * subtraction (- : int * int -> int) - * multiplication (* : int * int -> int) - * division (/ : int * int -> int, may fail) - * modulo (% : int * int -> int, may fail) - * comparison (<, >, >=, <=, =, <> : int * int -> int) - Comparison operators generally work on ints and strings. - (In)equality checks work on all values except for areas. - Note that "==" and "!=" are available as alternatives for "=" and - "<>" (respectively). - * conjunction and disjunction (&&, || : int * int -> int). Note - that these are not short-circuit. - * Bitwise or (| : int * int -> int) - * Bitwise and (& : int * int -> int) - * Bitwise xor (^ : int * int -> int) - * Shift left and right (<<, >> : int * int -> int) - -- Function applications: - Function applications are written as - - f(arg1, ..., argn) - - where each `argi' is an arbitrary expression. - For a complete list of Functions, see `functions' below. - -- Variable references: - The expression (foo + 1) references a variable `foo' which must have - been previously defined. - - -Anchors: --------- - Anchors are teleport anchors; spells can look them up by name. Each -teleport anchor is written as - - TELEPORT-ANCHOR : = - - For example: - - TELEPORT-ANCHOR tulimshar : "home" = @("new_3-1.gat", 26, 26) @+(10, 10) - - This maps the teleport anchor `tulimshar' to an area in new_3-1.gat -binds it to the name "home". The function `anchor' can look up -teleport anchors (cf. the function library). - - -Spells: -------- - Each spell is written either as - - [spell-modifiers] SPELL : = - - or as - - [spell-modifiers] SPELL ( : ) : = - - For example, - - SPELL random_dance : "zxr" = ... - - creates a spell `random_dance' and makes it available under the -invocation "zxr". The string `...' is not a valid ; we will -look at proper spelldefs below. An alternative example illustrates -the use of parameters: - - SPELL shout (message : STRING) : "zzx" = ... - - This defines a spell `shout' (bound to invocation "zzx") that takes -a parameter `message' of type STRING. Valid types are STRING and PC: -PC parameters are automatically translated into a player character -entity of that name, or mapped to the caster if the specified name -does not map to a player character or is missing. - - The list of spell modifiers is short: - - * SILENT means that the spell's invocation will never be broadcast, - not even obscured. - * LOCAL means that the spell is bound to the location it was cast at - and will not `travel' with the caster. - * NONMAGIC means that the spell is not affected by the caster's ability - to perform magic. Typically used for special (quest) keywords. - - Modifiers may be given in any order, but only once. - -- Spell bodies - - Spell bodies consist of three parts: LET bindings, spell guards and - effects. LET bindings locally bind names to values. Spell guards - are constraints that must be satisfied for the spell to be - triggered. Effects describes what happens if the spell is - successfully triggered. Spells may have multiple guards and - effects. - - Consider the following example: - - SPELL plugh (message : STRING) : "zzx" = - LET x = "kappa" - y = "sigma" - IN - (MANA 1, CATALYSTS ["Pearl"]) => EFFECT message (caster, "First branch"); - | (MANA 20) => EFFECT message (caster, "Second branch") - - This defines a spell `plugh' with two let bindings (`x' bound to - "kappa" and `y' bound to "sigma") and two `branches' which are - tested in sequence. The first branch tests whether the caster has - one spellpoint and owns a pearl-- if so, the effect of sending a - message "First branch" to the caster is triggered. - - However, if the spell guard is not satisfied, the magic engine - examines the second branch. Now, if the caster has 20 spellpoints, - we INSTEAD trigger the effect of sending the message "Second branch" - to the caster. - -- Spell guards - - Spell guards can be omitted; in that case, just use the effect - itself. Otherwise they can be any of the following: - * MANA x: Require x spellpoints to be present. If this spellguard - is taken, x mana will be consumed implicitly. This requirement is - cumulative. - * CASTTIME x: Require that the caster spend x milliseconds until - the next spell can be cast. This requirement is cumulative. If - the total casttime for a spell is less than the global variable - min_casttime, then the latter supercedes the specified spell cast - delay. - * REQUIRE : Test that the specified expression evaluates to - nonzero and does not fail. Requirements are cumulative. - * CATALYSTS : Ensure that the caster possesses all specified - items. This effect is cumulative. - * COMPONENTS : Ensure that the caster possesses all specified - items. If the branch suceeeds, all items specified here are - consumed. This effect is cumulative. - - Items can be specified as follows: - * [ 700, 701 ] -- require one item of item ID 700 and one of 701. - * [ 700, 3 * 701 ] -- require 1 item of item ID 700 and 3 of 701. - * [ "Pearl" ] -- require one item named `Pearl' in the item DB. - - Spell guards can be combined as follows: - * `or': disjunction. The first matching path is taken. - * (a, ..., n): conjunction. a, n, and everything in between must be - satisfied. - * a => b: Implication. If `a' is satisfied, try to satisfy `b'. - This operation is useful to combine different branches (see - below.) - - Different branches of spell effects are separated by the vertical - bar `|'. For example, - - SPELL plugh (message : STRING) : "zzx" = - MANA 5 => ( (CATALYSTS ["Pearl"]) => - EFFECT message (caster, "First branch"); - | (MANA 20) => - EFFECT message (caster, "Second branch");) - - will always try to deduct 5 Mana points but then make a choice of - whether to go to the first branch (if the player has `Pearl') or to - the second (if the player does not have `Pear' but has another 20 - spell points, for a total of 25 spell points, all of which will be - consumed in that case.) - - -- Effects - - Effects describe what happens when a spell is triggered. Each - spell has at least one EFFECT specification, which looks as follows: - - EFFECT [ ATTRIGGER ] [ ATEND ] - - The three parts are as follows: - * EFFECT: All effects here are executed as soon as the spell is - triggered. - * ATEND: All steps described here are executed when the spell - finishes. Note that the spell may remain active after all steps - from the EFFECT section have been taken; this happens when the spell - triggers a status change (using the `status_change' operation, - as described in the operations library). In that case the spell - will terminate only after all status changes have finished. - The ATEND section is not called when the spell finishes due to the - caster dying. - * ATTRIGGER: This section is used only for the `override_attack' - operation and described there. - - Before effects are executed, the engine defines the following - variables: - - * The parameter (if any) - * caster : entity (the caster of this spell) - * spellpower : int (the caster's spellpower, normally 6 -- 198) - * location : location (the location the spell is currently at) - * self_spell : spell (the current spell) - * self_invocation : invocation (the current spell instance) - - The engine can then execute the following effects from the effect list: - * SKIP; # a no-op - * ABORT; # Abort the spell, don't run ATEND, don't consume a - # trigger charge (cf. `override_attack') - * END; # Skip to the ATEND block - * WAIT ; # Wait milliseconds before continuing - * = ; # Set to the result of evaluating - * ( ... ) # Execute statements through in sequence - * IF # Test condition . If nonzero, - THEN # execute . Otherwise, - ELSE # execute . - # The `ELSE' branch can be omitted. - - * FOREACH IN DO - # Evaluate to an area, find all entities in - # the area that match , randomise this list, - # bind to each in turn and execute . - - Example: - - FOREACH ENTITY t IN rbox(location(caster), 20) - DO aggravate(t, 0, caster); - # This aggravates all entities within 20 paces of - # the caster to attack the caster. - - Valid values for are - + ENTITY : PC or mob - + PC - + MOB - + TARGET : mob, PC (but only if we are on a PvP map) - - * FOR = TO DO - # This will iterate from to (inclusively), bind - # to the current iteration value, and execute . - - * BREAK; - # This will break out of the current FOR loop, FOREACH loop, or - # procedure. - - * ( , ..., ); - # This executes an operation. See `Operations', below, for a - # list. - - * CALL ( , ..., ); - # This will execute a procedure, after binding the actual - # parameters from to to the formal procedure - # parameters. - - * { ... } - # This executes arbitrary eAthena script code. The following - # variables script variables are bound implicitly: - # - @caster_name$ is the name of the spellcaster - # - @caster and @target are also bound, to useless values (sorry.) - # - # By default, script popup boxes are sent to the caster. This can - # be overridden by setting the special `script_target' variable. - -Procedures: ------------ - Procedures are defined as - - PROCEDURE ( , ... , ) = - - For example, - - PROCEDURE testproc(x) = message(caster, "foo(" + x + )"); - y = 10; - x = 20; - - defines a procedure `testproc' with one formal parameter `x'. - Procedure execution is nonrecursive and uses dynamic scoping. The - latter means that it can modify variables in the caller's scope. In - the above example, the assignment to `y' will be visible to the - caller. The assignment to `x' however will be not be visible, since - that assignment goes to the parameter and is therefore limited in - scope to `testproc'. More precisely, - - EFFECT x = 0; y = 0; testproc(1); - message(caster, "x=" + x + ", y=" + y); - - would print - - foo(1) - x=0, y=10 - - (note how the update to x is isolated from the caller.) - -Functions: ----------- -This section documents the function API. - -The following functions are available: - - + max : int * int -> int - Pick the greater of two values. - - + min : int * int -> int - Lesser of two values. - - + is_in : location * area -> bool - Test whether a location is within an area. - - + if_then_else : bool * 'a * 'a -> 'a - Test a condition (first parameter). If the contition is nonzero, - return the second parameter, otherwise the third parameter. - - + skill : entity * int -> int - Get the skill level that the `entity' has for the skill id. - - + dex : entity -> int - + agi : entity -> int - + int : entity -> int - + vit : entity -> int - + str : entity -> int - + luk : entity -> int - + hp : entity -> int - + sp : entity -> int - + def : entity -> int - + mdef : entity -> int - + max_hp : entity -> int - + max_sp : entity -> int - + level : entity -> int - Status attributes. - - + dir : entity -> dir - Direction that the entity is currently facing. - - + not : int -> int - Logical negation. (NOT bitwise negation.) - - + neg : int -> int - Bitwise negation. - - + name_of : entity -> string - | spell -> string - Retrieves the name either of an entity or of a spell. - - + location : entity -> location - Determines the location that the specified entity presently - occupies. - - + random : int -> int - random(6) yields a random value from 0 to 5. - - + random_dir : int -> dir - random_dir(0) yields N, S, E, or W. - random_dir(1) yields N, S, E, W, SE, SW, NE, or NW. - - + hash_entity : entity -> int - Retrieve a number idenfying the entity. - - + is_married : entity -> int - Tests whether the entity is married. - - + partner : entity -> entity - Retrieves the entity's partner, if online, or fails otherwise. - - + awayfrom : location * dir * int -> location - awayfrom(loc, dir, distance) returns a location obtained by moving - at most `distance' towards `dir', starting at `loc'. If the move - hits an obstacle, we stop before the obstacle. - - + failed : 'a -> bool - True iff the input was the special failure value. - - + pc : string -> entity - Looks up a player character by name. Fails if there is no match. - - + npc : string -> entity - Looks up an NPC by name. Fails if there is no match. - - + distance : location * location -> int - This is the `fake square distance': The maximum of delta x and - delta y between the two locations. - - + rdistance : location * location -> int - This is the `real' distance (square root of the square of dx, dy) - - + anchor : string -> area - Looks up a teleport anchor by name and returns the associated - area. Fails if the result is not an area. - - + random_location : area -> location - Pick a random location from within an area. - - + script_int : entity * string -> int - Read a player script variable as an integer. - - + rbox : location * int -> area - rbox(l, n) computes rectangular box centered at `l', with a - `radius' of n. The box contains (n*2 + 1)^2 squares. - - + count_item : entity * int -> int - | entity * string -> int - Counts the number of instances of the specified item that the - entity has. Items may be given by ID or by name. - - + line_of_sight : location * location -> int - Determines whether there is a line-of-sight connection between the - two locations. - - + running_status_update : entity * int -> bool - Determines whether the specified status update is still active. - - + element : entity -> int - Determines what element the entity is associated with - - + element_level : entity -> int - Determines what element level the entity has - - + has_shroud : entity -> int - Determines whether the player is presently shrouded (i.e., whether - the player's name is being obscured.) - - + is_equipped : entity * int -> int - : entity * string -> int - Determines whether the player has equipped the specified item - - + spell_index : spell -> int - Determines a unique index assigned to each spell - - + is_exterior : location -> bool - Determines whether the location is under an open sky - - + contains_string : string * string -> bool - contains_string(a, b) determines whether the string `a' contains - the string `b' as a substring. - - + strstr : string * string -> bool - strstr(a, b) returns the offset of the first instance of the - string `b' in the string `a', or fails if there is none. The - offset is reported with a base of zero, i.e., strstr("xyz", "x") = 0. - - + strlen : string -> int - Compute the length of a string, in characters. - - + substr : string * int * int -> string - substr(s, offset, len) extracts a substring of the length `len' at - offset `offset'. The substring is automatically clipped, i.e., the - function will never fail. - - + sqrt : int -> int - Computes the square root of the specified number - - + map_level : location -> int - Determines the map level: 0 for outside, 1 for inside, 2ff for - dungeon levels (going down) - - + map_nr : location -> int - Computes the map number. Map number and map level together uniquely - identify a map. - - + dir_towards : location * location * int -> dir - dir_towards(start, end, flag) computes the direction from `start' to - `end'. If flag is zero, directions are limited to N, S, E, W; - otherwise NE, SE, NW, SW will also be used. The two locations must be - on the same map. - - + is_dead : entity -> bool - Determines whether the specified entity is no longer alive. - - + extract_healer_experience : entity * int -> int - Extracts `healer experience points' from the specified entity. - Non-PCs always have an empty pool of healer exprerience points. - PCs gain such experience points by killing/completing quests, - though this `healer experience point pool' slowly drains itself. - extract_healer_experience(pc, xp) extracts up to `xp' points. - - + is_pc : entity -> bool - Determines whether the target is a player character - -Operations: ------------ -This section documents the operations API. - - + sfx : entity * int * int -> () - | location * int *int -> () - Trigger a special effect (specified by sfx ID) for an entity or a - location. The int specifies a delay until the effect is issued. - - + itemheal : entity * int * int -> () - itemheal(entity, hp, sp) triggers item healing. This will - hopefully be slowed down if an appropriate server patch is - installed. - - + instaheal : entity * int * int -> () - itemheal(entity, hp, sp) heals instantly. - - + shroud : entity * int -> () - shroud(entity, flags) hides the entity's name (only for PCs). - Flags: 0x01: Hide PC'ss name when talking - 0x02: Shroud vanishes when player picks something up - 0x04: Shroud vanishes when player talks - The shroud will not affect players in visible range until the - entity has left and re-entered their field of vision. This can be - enforced by warping. - - + unshroud : entity -> () - Counter a shroud. - - + message : entity * string -> () - Send a message to the entity. - - + messenger_npc : location * int - * string * string * int -> () - messenger_npc(location, image, npc_name, message, duration) - creates a messenger NPC looking like the `image' at `location', - with name `npc_name' and delivering the `message' when clicked - upon. The NPC disappears after the `duration' in ms has expired. - - + move : entity * dir -> () - Move the entity into the specified direction, unless that - direction is blocked. - - + warp : entity * location -> () - Warp entity to specified location. - - + spawn : area * entity * int * int * int * int -> () - spawn(area, owner, mob_id, attitude, count, lifetime) spawns for a - limited `lifetime' (in ms) a total of `count' monsters of kind - `mob_id' in `area'. `attitude' specifies how they behave: - 0 : attack everyone - 1 : be friendly - 2 : attack enemies of `owner' and give XP to `owner' if successful - - + banish : entity -> () - If the entity was spawned by the `spawn' operation, eliminate it. - - - + status_change : entity * int * int - * int * int * int * int -> () - status_change(entity, status, v1, v2, v3, v4, duration) initiates - a status change. The precise status change (and the meaning of - `v1', `v2', `v3', `v4') varies depending on `status'. This - operation may delay spell termination (and the ATEND effect). - ATEND can therefore be used to notify the caster that the status - change has finished. - - + stop_status_change : entity * int - Stops a status change - - + override_attack : entity * int * int - * int * int *int * int -> () - override_attack(entity, charges, delay, range, icon, animation, stop) - overrides the entity's current attack (only for PCs). The entity - will have `charges' attacks that instead result in calling the - ATTRIGGER effect for this spell. When this operation is called, - the spell environment (variables etc.) is cloned into a separate - invocation. This special invocation will be triggered every time - the entity tries to attack, until they have run out of charages (at - which time the previous behaviour is restored.) - `delay' specifies the attack delay. - `range' specifies the attack range as shown in the client GUI. - `icon' is the ID of a status-effect ID that will be displayed - while the spell is in effect. - `animation' is the attack animation ID that should be used. - `stop' decides whether attacking should stop once the spell is out - of charges (1) or continue (0). (Note: this doesn't properly work, - possibly due to client issues. Always use 0.) - - Note that if the ATTRIGGER effect ABORTs, no charge will be - consumed. - - ATTRIGGER can refernece the player's target via the `target' - variable. - - Example: - - SPELL tshuffle : "zvp" = - (MANA 1, CATALYSTS ["Pearl"]) => - EFFECT - override_attack(caster, 3, 500, 10, 700, 31); - ATTRIGGER - IF (not (line_of_sight(location, location(target)))) - THEN (message (caster, "No line of sight!"); ABORT;) - FOR i = 0 TO 10 DO (move(target, random_dir(0));); - - This overrides the caster's attack for three attacks (attack delay - 500, range 10) to instead randomly move around any entity that the - player tries to attack by up to 11 paces. - - + create_item : entity * int * int -> () - | entity * string * int -> () - create_item(entity, item, count) gives the `entity' `count' - instances of `item'. As usual, `item' can be either a string name - or an item ID. - - + aggravate : entity * int * entity -> () - aggravate (victim, mode, target) causes the `victim' to - mode = 0: attack `target' - mode = 1: become universally permanently aggressive - mode = 2: both of the above - - + injure : entity * entity * int * int -> () - injure(attacker, defender, hp, sp) causes damage to the defender - from the attacker (possibly killing, giving XP etc.), unless the - defender is immortal (NPC) or a PC on a non-PvP map. - - + emote : entity * int -> () - Issues the specified emotion to the specified entity. - - + set_script_variable : entity * string * int -> () - Sets a script variable to the specified value - - + set_hair_colour : entity * int -> () - Sets the hair colour of the specified entity to the specified - value (must be a PC). - - + set_hair_style : entity * int -> () - Adjusts the hair style of a PC. - - + drop_item : location * (int | string) * int * int -> () - drop_item(place, "name", count, duration) drops `count' items - (where count may be zero) of name "name" at `place'. The items - vanish again after `duration'. - - + drop_item_for : location * (int | string) - * int * int * entity * int -> () - drop_item_for(place, obj, count, duration, owner, delay) works - like drop_item(place, obj, count, duration), except that the item - can only be picked up by `owner' for the next `delay' - milliseconds, modulo pickup rules (spousal pickup, out-of-range). - - + gain_experience : entity * int * int * int -> () - gain_experience(player, base_xp, job_xp, reason) gives expereince - points to `player'. If reason is 0, they are handed out normally, - but if reason is 1, then those experience points are NOT added to - the pool of experience points that healers can draw from `player'. - - -Script API updates: -------------------- -Two new script API functions are available: - - * getspellinvocation : string -> string - Looks up a spell by spell ID and returns the spell's invocation. - * getanchorinvocation : string -> string - Looks up a teleport anchor by anchor ID and returns the invocation. - - -Syntax Reference: ------------------ - -SPELLCONF ::= (GLOBAL | ANCHOR | SPELL | PROCEDURE | ';')* (* The ';' are only for decorative purposes *) - - VALUE ::= - | - | - | - | (* one of {N, S, E, W, NE, SE, NW, SW} *) - - EXPR ::= (VALUE) - | (AREA) - | (EXPR) '+' (EXPR) - | (EXPR) '*' (EXPR) - | (EXPR) '-' (EXPR) - | (EXPR) '/' (EXPR) - | (EXPR) '%' (EXPR) - | (EXPR) '<' (EXPR) - | (EXPR) '>' (EXPR) - | (EXPR) '<>' (EXPR) - | (EXPR) '=' (EXPR) - | (EXPR) '!=' (EXPR) - | (EXPR) '==' (EXPR) - | (EXPR) '<=' (EXPR) - | (EXPR) '>=' (EXPR) - | (EXPR) '&&' (EXPR) - | (EXPR) '||' (EXPR) - | (EXPR) '|' (EXPR) - | (EXPR) '^' (EXPR) (* XOR *) - | (EXPR) '&' (EXPR) (* binary AND *) - | (EXPR) '<<' (EXPR) - | (EXPR) '>>' (EXPR) - | '(' ((EXPR) /* ',') ')' - | - | '(' (EXPR) ')' - - INVOCATION ::= (* used for convenience *) - - LOCATION ::= '@' '(' (EXPR) ',' (EXPR) ',' (EXPR) ')' (* *) - - AREA ::= (LOCATION) - | (LOCATION) '@' '+' '(' (EXPR) ',' (EXPR) ')' (* width and height around location, passable only *) - | (LOCATION) 'towards' (EXPR) ':' '(' (EXPR) ',' (EXPR) ')' (* towards dir: Bar-shaped, width and depth; only NSEW supported *) - - ITEMS ::= '[' (ITEMC) /+ ',' ']' - - ITEMC ::= (ITEM) - | '*' (ITEM) - - ITEM ::= - | - -* global - - GLOBAL ::= 'CONST'? '=' (EXPR) ';' - - Available globals: - - min_casttime : int (* min # of ticks for each spell (BEFORE scaling according to battle.conf) *) - - obscure : int (* obscure percentage *) - -* teleport-anchor - - ANCHOR ::= 'TELEPORT-ANCHOR' ':' (INVOCATION) '=' (EXPR) - - For example, - - TELEPORT-ANCHOR t : "tulimshar" = @("map_3-1.gat", 44, 70) - - creates a teleport anchor with name `t' and invocation `tulimshar' at the speicfied location. - -* spell - - SPELL ::= spellmod 'SPELL' (ARG)? ':' (INVOCATION) '=' (SPELLDEF) - - SPELLMOD ::= ('SILENT' | 'LOCAL')* - - (silent: invocation silently dropped. local: `location' variable doesn't change after spell init.) - - ARG ::= '(' ':' (ARGTYPE) ')" - - ARGTYPE ::= 'PC' (* yields a pc, or self *) - | 'STRING' - - SPELLDEF ::= ((SPELLBODY) /* '|') - | 'LET' (DECL)* 'IN' ((SPELLBODY) /* '|') - - SPELLBODY ::= (SPELLGUARD) '=>' (SPELLBODY) - | 'EFFECT' (EFFECT) ('ATTRIGGER' (EFFECT))? ('ATEND' (EFFECT))? - - (* `ATTRIGGER' is executed as a separate effect if the spell is `triggered'. If the trigger has a target, it is denoted - * in the variable `target'. - * `ATEND' is invoked for normal termination, i.e., after the spell is over or has been dispelled. `ATEND' is NOT used - * for the triggered effect; instead, spawn a separate spell in ATTRIGGER if that is desired. *) - - DECL ::= '=' (EXPR) ';' - - SPELLGUARD ::= (PREREQ) - | (SPELLGUARD) 'or' (SPELLGUARD) - | '(' (SPELLGUARD) /+ ',' ')' - - PREREQ ::= 'REQUIRE' (EXPR) - | 'CATALYSTS' (ITEMS) - | 'COMPONENTS' (ITEMS) - | 'MANA' (EXPR) - | 'CASTTIME' (EXPR) (* # of ticks until the next spell can be cast *0 - - EFFECT ::= EFFECT ';' EFFECT - | '(' (EFFECT) ')' - | 'SKIP' (* no-op *) - | 'ABORT' (* abort spell. If used in a trigger, no charges will be consumed. *) - | 'END' (* skip to atend *) - | 'BREAK' (* break out of loop *) - | '=' (EXPR) (* interpreted in the current context, eval'ds trictly *) - | 'FOREACH' (SELECTION) 'IN' (AREA) 'DO' (EFFECT) (* randomises the subjects first *) - | 'FOR' '=' (EXPR) 'TO' (EXPR) 'DO' (EFFECT) (* bounds are evaluated when the loop begins, no updates are respected *) - | 'IF' (EXPR) 'THEN' (EFFECT) ('ELSE' (EFFECT))? - | 'WAIT' (EXPR) (* amount in ticks before continuing. *) - | '(' (EXPR) /* ',' ')' (* operation *) - | 'CALL' '(' (EXPR) /* ',' ')' (* procedure call *) - | '{' ... '}' (* script *) - - SELECTION ::= 'PC' - | 'MOB' - | 'ENTITY' (* MOB or PC *) - | 'TARGET' (* like ENTITY, but includes PCs only on PvP maps *) - | 'SPELL' - | 'NPC' - -* procedures - - Procedures may be invoked via `CALL'. They use dynamic scoping. - - PROCEDURE ::= 'PROCEDURE' '(' /* ',' ')' '=' (EFFECT) diff --git a/old-doc/mapserver-logging b/old-doc/mapserver-logging new file mode 100644 index 0000000..573c61f --- /dev/null +++ b/old-doc/mapserver-logging @@ -0,0 +1,71 @@ +Log format: + +LOGLINE ::= int'.'int + +MESSAGE ::= 'log-start' + | 'log-start v2' + | 'log-start v3' + | 'log-start v4' + | + | + +COORD ::= int':'int','int + +PC ::= 'PC'int + +MOB ::= 'MOB'int + +TARGET ::= | + +SPELLRESULT ::= 'SUCCESS' | 'FAILURE' + +STATPLACE ::= 'LOGIN' | 'STATUP' | 'STATUP2' | 'STATRESET' + +XPPLACE ::= 'LOGIN' | 'LEVELUP' + +XPREASON ::= 'SCRIPTXP' | 'HEALXP' | 'KILLXP' | 'SHAREXP' | 'UNKNOWNXP' + +ZEROTARGET ::= 'null' | + +MOB-MESSAGE ::= 'DEAD' + +PC-MESSAGE ::= 'WPNDMG' int 'FOR' int WPN int # "WPNDMG MOB01 type FOR damage WPN weapon-item" + | 'WPNINJURY' int 'FOR' int + | 'MOB-TO-MOB-DMG' 'FROM' int 'TO' int 'FOR' int # Summoned monster damage + | 'SPELLHEAL-INSTA' FOR int + | 'SPELLDMG' FOR int BY spell-id + | 'CAST' spell-id + | 'STAT' int int int int int int # str agi vit int dex luk + | 'XP' int int 'JOB' int int int 'ZENY' int '+' int # level xp job-level skill-xp skill-pts zeny bank-zeny + | 'SKILLPOINTS-UP' int # new number of unused skill points + | 'MAGIC' int int int int int int 'EXP' int int 'LOGIN' # magic life war transmute nature ether magic-xp healer-xp + | 'SKILL-ACTIVATE' int int int # skillid skilllvl skillpower + | 'SKILL-UP' int int int # skillid skilllvl skillpower + | 'SKILL-DEACTIVATE' int + | 'MAGIC-ABSORB-DMG' int + | 'GAINXP' int + | 'PICKUP' int int # item-id count + | 'INJURED-BY' 'FOR' int + | 'DEAD' + | 'LOGOUT' + | 'TRADECOMMIT' 'WITH' int 'GIVE' int 'GET' int # pc-id give-zeny get-zeny + | 'TRADECANCEL' + | 'TRADEOK' + | 'STATUP' + | 'STATRESET' + | 'STATUP2' + +NOTES: +------ +1. Any PICKUP between TRADECOMMIT and TRADEOK is part of the trade. +2. TRADECOMMIT is issued for both participants in a trade. +3. INJURED-BY seems to only duplicate information we already have. + + +Illegal items + +645, 668, 659, 731, 734, 744, 745, 755, 756, 757, 747, 748, 721, 722 + +Restricted access + +725, 726, 647, 646 diff --git a/old-doc/old-spell-language b/old-doc/old-spell-language new file mode 100644 index 0000000..5f70f89 --- /dev/null +++ b/old-doc/old-spell-language @@ -0,0 +1,870 @@ +-------------------------------------------------------------------------------- +== Spell language + +* Comments: One-line comments using '#' or '//' as prefix +* names vs. invocations: spells and anchors have both names + and invocations. Names are used to refer to spells and + anchors from within scripts without revealing their + invocations. + + +Each spell specification file consists of a sequence of definitions of +globals, anchors, spells, and procedures. + +Types: +------ + These are the primitive types: + * int + * string + * dir (a direction: N, S, SE, NW etc.) + * location (a single spot in the world) + * entity (a PC, NPC, or monster) + * area (a set of locations) + * spell (a spell) + * invocation (a spell instance) + * fail (a separate type that arises implicitly when a function + fails) + + `fail' arises in special circumstances in some functions, e.g. when +trying to divide by zero, or when function parameters fail to +type-check. When a fail value flows into an operation, than that +operation is skipped. When a fail value flows into a function, then +that function evaluates to fail, except for + + * if_then_else (which permits `fail' in its true or false branch) + * failed (which returns true iff the argument is of type fail). + + We will use standard functional type notation: + + `int * string -> entity' denotes the type of a function that accepts +an integer and a string and returns an entity. We use the return `()' +for operations (which return nothing). + +Globals: +-------- + A `global' is a global variable, declared and defined in the +same line: + + foo = "this is a string"; + + Expressions can be arbitrary language expressions. Note that globals +can only reference the values of globals defined earlier in the same +program. + + There are two special globals: + + * min_casttime : int + Minimal number of milliseconds of cast delay, no matter what any + given spell may say. Cast delay is the time before the next spell + can be cast. + + * obscure : int + Chance of a character of a given spell to be obscured (masked out + by an asterisk). + + + Globals can also be defined as CONST (though this should not be done +for the special globals listed above or they will not take effect.) +CONST-defined globals cannot be re-defined. + + +Expressions: +------------ + Expressions occur in globals, anchors, spells, and procedures. +Expressions evaluate to values of a given type. Expressions can be +simple literals, area literals, infix expressions, function +applications, or variable refecences. + +- Simple literals: + * 42, 0xff: int literals + * N, S, E, W, NE, NW, SE, SW : dir literals + * "foo bar" : string literals + +- Area literals: + * @("new_3-1.gat", 26, 26) + This area denotes a single field on a map (new_3-1.gat, + co-ordinates (26,26). + * @("new_3-1.gat", 26, 26) @+ (10, 10) + This area is 100 fields, stretching to a field of size 10x10 to + the south and east of (26,26) in new_3-1.gat. + * @("new_3-1.gat", 26, 26) towards S (5, 3) + This area is a rectangular field of depth 3 and width 5 to the + south of (26,26) in new_3-1.gat. `depth 3' here means that it + stretches three fields to the south. `width 5' here means that it + extends five fields to the east and five to the west of the + specified coordinate, for a total width of 11. + The only directions supported here are S, E, N, W. + +- Infix expressions: + Infix expressions are special functions in which the operator is + written between two expressions. Infix expressions largely follow + the C precedence rules. The following infix operators are + supported: + * addition (+) (operates on ints, concatenates strings, and + constructs the union of areas) + * subtraction (- : int * int -> int) + * multiplication (* : int * int -> int) + * division (/ : int * int -> int, may fail) + * modulo (% : int * int -> int, may fail) + * comparison (<, >, >=, <=, =, <> : int * int -> int) + Comparison operators generally work on ints and strings. + (In)equality checks work on all values except for areas. + Note that "==" and "!=" are available as alternatives for "=" and + "<>" (respectively). + * conjunction and disjunction (&&, || : int * int -> int). Note + that these are not short-circuit. + * Bitwise or (| : int * int -> int) + * Bitwise and (& : int * int -> int) + * Bitwise xor (^ : int * int -> int) + * Shift left and right (<<, >> : int * int -> int) + +- Function applications: + Function applications are written as + + f(arg1, ..., argn) + + where each `argi' is an arbitrary expression. + For a complete list of Functions, see `functions' below. + +- Variable references: + The expression (foo + 1) references a variable `foo' which must have + been previously defined. + + +Anchors: +-------- + Anchors are teleport anchors; spells can look them up by name. Each +teleport anchor is written as + + TELEPORT-ANCHOR : = + + For example: + + TELEPORT-ANCHOR tulimshar : "home" = @("new_3-1.gat", 26, 26) @+(10, 10) + + This maps the teleport anchor `tulimshar' to an area in new_3-1.gat +binds it to the name "home". The function `anchor' can look up +teleport anchors (cf. the function library). + + +Spells: +------- + Each spell is written either as + + [spell-modifiers] SPELL : = + + or as + + [spell-modifiers] SPELL ( : ) : = + + For example, + + SPELL random_dance : "zxr" = ... + + creates a spell `random_dance' and makes it available under the +invocation "zxr". The string `...' is not a valid ; we will +look at proper spelldefs below. An alternative example illustrates +the use of parameters: + + SPELL shout (message : STRING) : "zzx" = ... + + This defines a spell `shout' (bound to invocation "zzx") that takes +a parameter `message' of type STRING. Valid types are STRING and PC: +PC parameters are automatically translated into a player character +entity of that name, or mapped to the caster if the specified name +does not map to a player character or is missing. + + The list of spell modifiers is short: + + * SILENT means that the spell's invocation will never be broadcast, + not even obscured. + * LOCAL means that the spell is bound to the location it was cast at + and will not `travel' with the caster. + * NONMAGIC means that the spell is not affected by the caster's ability + to perform magic. Typically used for special (quest) keywords. + + Modifiers may be given in any order, but only once. + +- Spell bodies + + Spell bodies consist of three parts: LET bindings, spell guards and + effects. LET bindings locally bind names to values. Spell guards + are constraints that must be satisfied for the spell to be + triggered. Effects describes what happens if the spell is + successfully triggered. Spells may have multiple guards and + effects. + + Consider the following example: + + SPELL plugh (message : STRING) : "zzx" = + LET x = "kappa" + y = "sigma" + IN + (MANA 1, CATALYSTS ["Pearl"]) => EFFECT message (caster, "First branch"); + | (MANA 20) => EFFECT message (caster, "Second branch") + + This defines a spell `plugh' with two let bindings (`x' bound to + "kappa" and `y' bound to "sigma") and two `branches' which are + tested in sequence. The first branch tests whether the caster has + one spellpoint and owns a pearl-- if so, the effect of sending a + message "First branch" to the caster is triggered. + + However, if the spell guard is not satisfied, the magic engine + examines the second branch. Now, if the caster has 20 spellpoints, + we INSTEAD trigger the effect of sending the message "Second branch" + to the caster. + +- Spell guards + + Spell guards can be omitted; in that case, just use the effect + itself. Otherwise they can be any of the following: + * MANA x: Require x spellpoints to be present. If this spellguard + is taken, x mana will be consumed implicitly. This requirement is + cumulative. + * CASTTIME x: Require that the caster spend x milliseconds until + the next spell can be cast. This requirement is cumulative. If + the total casttime for a spell is less than the global variable + min_casttime, then the latter supercedes the specified spell cast + delay. + * REQUIRE : Test that the specified expression evaluates to + nonzero and does not fail. Requirements are cumulative. + * CATALYSTS : Ensure that the caster possesses all specified + items. This effect is cumulative. + * COMPONENTS : Ensure that the caster possesses all specified + items. If the branch suceeeds, all items specified here are + consumed. This effect is cumulative. + + Items can be specified as follows: + * [ 700, 701 ] -- require one item of item ID 700 and one of 701. + * [ 700, 3 * 701 ] -- require 1 item of item ID 700 and 3 of 701. + * [ "Pearl" ] -- require one item named `Pearl' in the item DB. + + Spell guards can be combined as follows: + * `or': disjunction. The first matching path is taken. + * (a, ..., n): conjunction. a, n, and everything in between must be + satisfied. + * a => b: Implication. If `a' is satisfied, try to satisfy `b'. + This operation is useful to combine different branches (see + below.) + + Different branches of spell effects are separated by the vertical + bar `|'. For example, + + SPELL plugh (message : STRING) : "zzx" = + MANA 5 => ( (CATALYSTS ["Pearl"]) => + EFFECT message (caster, "First branch"); + | (MANA 20) => + EFFECT message (caster, "Second branch");) + + will always try to deduct 5 Mana points but then make a choice of + whether to go to the first branch (if the player has `Pearl') or to + the second (if the player does not have `Pear' but has another 20 + spell points, for a total of 25 spell points, all of which will be + consumed in that case.) + + +- Effects + + Effects describe what happens when a spell is triggered. Each + spell has at least one EFFECT specification, which looks as follows: + + EFFECT [ ATTRIGGER ] [ ATEND ] + + The three parts are as follows: + * EFFECT: All effects here are executed as soon as the spell is + triggered. + * ATEND: All steps described here are executed when the spell + finishes. Note that the spell may remain active after all steps + from the EFFECT section have been taken; this happens when the spell + triggers a status change (using the `status_change' operation, + as described in the operations library). In that case the spell + will terminate only after all status changes have finished. + The ATEND section is not called when the spell finishes due to the + caster dying. + * ATTRIGGER: This section is used only for the `override_attack' + operation and described there. + + Before effects are executed, the engine defines the following + variables: + + * The parameter (if any) + * caster : entity (the caster of this spell) + * spellpower : int (the caster's spellpower, normally 6 -- 198) + * location : location (the location the spell is currently at) + * self_spell : spell (the current spell) + * self_invocation : invocation (the current spell instance) + + The engine can then execute the following effects from the effect list: + * SKIP; # a no-op + * ABORT; # Abort the spell, don't run ATEND, don't consume a + # trigger charge (cf. `override_attack') + * END; # Skip to the ATEND block + * WAIT ; # Wait milliseconds before continuing + * = ; # Set to the result of evaluating + * ( ... ) # Execute statements through in sequence + * IF # Test condition . If nonzero, + THEN # execute . Otherwise, + ELSE # execute . + # The `ELSE' branch can be omitted. + + * FOREACH IN DO + # Evaluate to an area, find all entities in + # the area that match , randomise this list, + # bind to each in turn and execute . + + Example: + + FOREACH ENTITY t IN rbox(location(caster), 20) + DO aggravate(t, 0, caster); + # This aggravates all entities within 20 paces of + # the caster to attack the caster. + + Valid values for are + + ENTITY : PC or mob + + PC + + MOB + + TARGET : mob, PC (but only if we are on a PvP map) + + * FOR = TO DO + # This will iterate from to (inclusively), bind + # to the current iteration value, and execute . + + * BREAK; + # This will break out of the current FOR loop, FOREACH loop, or + # procedure. + + * ( , ..., ); + # This executes an operation. See `Operations', below, for a + # list. + + * CALL ( , ..., ); + # This will execute a procedure, after binding the actual + # parameters from to to the formal procedure + # parameters. + + * { ... } + # This executes arbitrary eAthena script code. The following + # variables script variables are bound implicitly: + # - @caster_name$ is the name of the spellcaster + # - @caster and @target are also bound, to useless values (sorry.) + # + # By default, script popup boxes are sent to the caster. This can + # be overridden by setting the special `script_target' variable. + +Procedures: +----------- + Procedures are defined as + + PROCEDURE ( , ... , ) = + + For example, + + PROCEDURE testproc(x) = message(caster, "foo(" + x + )"); + y = 10; + x = 20; + + defines a procedure `testproc' with one formal parameter `x'. + Procedure execution is nonrecursive and uses dynamic scoping. The + latter means that it can modify variables in the caller's scope. In + the above example, the assignment to `y' will be visible to the + caller. The assignment to `x' however will be not be visible, since + that assignment goes to the parameter and is therefore limited in + scope to `testproc'. More precisely, + + EFFECT x = 0; y = 0; testproc(1); + message(caster, "x=" + x + ", y=" + y); + + would print + + foo(1) + x=0, y=10 + + (note how the update to x is isolated from the caller.) + +Functions: +---------- +This section documents the function API. + +The following functions are available: + + + max : int * int -> int + Pick the greater of two values. + + + min : int * int -> int + Lesser of two values. + + + is_in : location * area -> bool + Test whether a location is within an area. + + + if_then_else : bool * 'a * 'a -> 'a + Test a condition (first parameter). If the contition is nonzero, + return the second parameter, otherwise the third parameter. + + + skill : entity * int -> int + Get the skill level that the `entity' has for the skill id. + + + dex : entity -> int + + agi : entity -> int + + int : entity -> int + + vit : entity -> int + + str : entity -> int + + luk : entity -> int + + hp : entity -> int + + sp : entity -> int + + def : entity -> int + + mdef : entity -> int + + max_hp : entity -> int + + max_sp : entity -> int + + level : entity -> int + Status attributes. + + + dir : entity -> dir + Direction that the entity is currently facing. + + + not : int -> int + Logical negation. (NOT bitwise negation.) + + + neg : int -> int + Bitwise negation. + + + name_of : entity -> string + | spell -> string + Retrieves the name either of an entity or of a spell. + + + location : entity -> location + Determines the location that the specified entity presently + occupies. + + + random : int -> int + random(6) yields a random value from 0 to 5. + + + random_dir : int -> dir + random_dir(0) yields N, S, E, or W. + random_dir(1) yields N, S, E, W, SE, SW, NE, or NW. + + + hash_entity : entity -> int + Retrieve a number idenfying the entity. + + + is_married : entity -> int + Tests whether the entity is married. + + + partner : entity -> entity + Retrieves the entity's partner, if online, or fails otherwise. + + + awayfrom : location * dir * int -> location + awayfrom(loc, dir, distance) returns a location obtained by moving + at most `distance' towards `dir', starting at `loc'. If the move + hits an obstacle, we stop before the obstacle. + + + failed : 'a -> bool + True iff the input was the special failure value. + + + pc : string -> entity + Looks up a player character by name. Fails if there is no match. + + + npc : string -> entity + Looks up an NPC by name. Fails if there is no match. + + + distance : location * location -> int + This is the `fake square distance': The maximum of delta x and + delta y between the two locations. + + + rdistance : location * location -> int + This is the `real' distance (square root of the square of dx, dy) + + + anchor : string -> area + Looks up a teleport anchor by name and returns the associated + area. Fails if the result is not an area. + + + random_location : area -> location + Pick a random location from within an area. + + + script_int : entity * string -> int + Read a player script variable as an integer. + + + rbox : location * int -> area + rbox(l, n) computes rectangular box centered at `l', with a + `radius' of n. The box contains (n*2 + 1)^2 squares. + + + count_item : entity * int -> int + | entity * string -> int + Counts the number of instances of the specified item that the + entity has. Items may be given by ID or by name. + + + line_of_sight : location * location -> int + Determines whether there is a line-of-sight connection between the + two locations. + + + running_status_update : entity * int -> bool + Determines whether the specified status update is still active. + + + element : entity -> int + Determines what element the entity is associated with + + + element_level : entity -> int + Determines what element level the entity has + + + has_shroud : entity -> int + Determines whether the player is presently shrouded (i.e., whether + the player's name is being obscured.) + + + is_equipped : entity * int -> int + : entity * string -> int + Determines whether the player has equipped the specified item + + + spell_index : spell -> int + Determines a unique index assigned to each spell + + + is_exterior : location -> bool + Determines whether the location is under an open sky + + + contains_string : string * string -> bool + contains_string(a, b) determines whether the string `a' contains + the string `b' as a substring. + + + strstr : string * string -> bool + strstr(a, b) returns the offset of the first instance of the + string `b' in the string `a', or fails if there is none. The + offset is reported with a base of zero, i.e., strstr("xyz", "x") = 0. + + + strlen : string -> int + Compute the length of a string, in characters. + + + substr : string * int * int -> string + substr(s, offset, len) extracts a substring of the length `len' at + offset `offset'. The substring is automatically clipped, i.e., the + function will never fail. + + + sqrt : int -> int + Computes the square root of the specified number + + + map_level : location -> int + Determines the map level: 0 for outside, 1 for inside, 2ff for + dungeon levels (going down) + + + map_nr : location -> int + Computes the map number. Map number and map level together uniquely + identify a map. + + + dir_towards : location * location * int -> dir + dir_towards(start, end, flag) computes the direction from `start' to + `end'. If flag is zero, directions are limited to N, S, E, W; + otherwise NE, SE, NW, SW will also be used. The two locations must be + on the same map. + + + is_dead : entity -> bool + Determines whether the specified entity is no longer alive. + + + extract_healer_experience : entity * int -> int + Extracts `healer experience points' from the specified entity. + Non-PCs always have an empty pool of healer exprerience points. + PCs gain such experience points by killing/completing quests, + though this `healer experience point pool' slowly drains itself. + extract_healer_experience(pc, xp) extracts up to `xp' points. + + + is_pc : entity -> bool + Determines whether the target is a player character + +Operations: +----------- +This section documents the operations API. + + + sfx : entity * int * int -> () + | location * int *int -> () + Trigger a special effect (specified by sfx ID) for an entity or a + location. The int specifies a delay until the effect is issued. + + + itemheal : entity * int * int -> () + itemheal(entity, hp, sp) triggers item healing. This will + hopefully be slowed down if an appropriate server patch is + installed. + + + instaheal : entity * int * int -> () + itemheal(entity, hp, sp) heals instantly. + + + shroud : entity * int -> () + shroud(entity, flags) hides the entity's name (only for PCs). + Flags: 0x01: Hide PC'ss name when talking + 0x02: Shroud vanishes when player picks something up + 0x04: Shroud vanishes when player talks + The shroud will not affect players in visible range until the + entity has left and re-entered their field of vision. This can be + enforced by warping. + + + unshroud : entity -> () + Counter a shroud. + + + message : entity * string -> () + Send a message to the entity. + + + messenger_npc : location * int + * string * string * int -> () + messenger_npc(location, image, npc_name, message, duration) + creates a messenger NPC looking like the `image' at `location', + with name `npc_name' and delivering the `message' when clicked + upon. The NPC disappears after the `duration' in ms has expired. + + + move : entity * dir -> () + Move the entity into the specified direction, unless that + direction is blocked. + + + warp : entity * location -> () + Warp entity to specified location. + + + spawn : area * entity * int * int * int * int -> () + spawn(area, owner, mob_id, attitude, count, lifetime) spawns for a + limited `lifetime' (in ms) a total of `count' monsters of kind + `mob_id' in `area'. `attitude' specifies how they behave: + 0 : attack everyone + 1 : be friendly + 2 : attack enemies of `owner' and give XP to `owner' if successful + + + banish : entity -> () + If the entity was spawned by the `spawn' operation, eliminate it. + + + + status_change : entity * int * int + * int * int * int * int -> () + status_change(entity, status, v1, v2, v3, v4, duration) initiates + a status change. The precise status change (and the meaning of + `v1', `v2', `v3', `v4') varies depending on `status'. This + operation may delay spell termination (and the ATEND effect). + ATEND can therefore be used to notify the caster that the status + change has finished. + + + stop_status_change : entity * int + Stops a status change + + + override_attack : entity * int * int + * int * int *int * int -> () + override_attack(entity, charges, delay, range, icon, animation, stop) + overrides the entity's current attack (only for PCs). The entity + will have `charges' attacks that instead result in calling the + ATTRIGGER effect for this spell. When this operation is called, + the spell environment (variables etc.) is cloned into a separate + invocation. This special invocation will be triggered every time + the entity tries to attack, until they have run out of charages (at + which time the previous behaviour is restored.) + `delay' specifies the attack delay. + `range' specifies the attack range as shown in the client GUI. + `icon' is the ID of a status-effect ID that will be displayed + while the spell is in effect. + `animation' is the attack animation ID that should be used. + `stop' decides whether attacking should stop once the spell is out + of charges (1) or continue (0). (Note: this doesn't properly work, + possibly due to client issues. Always use 0.) + + Note that if the ATTRIGGER effect ABORTs, no charge will be + consumed. + + ATTRIGGER can refernece the player's target via the `target' + variable. + + Example: + + SPELL tshuffle : "zvp" = + (MANA 1, CATALYSTS ["Pearl"]) => + EFFECT + override_attack(caster, 3, 500, 10, 700, 31); + ATTRIGGER + IF (not (line_of_sight(location, location(target)))) + THEN (message (caster, "No line of sight!"); ABORT;) + FOR i = 0 TO 10 DO (move(target, random_dir(0));); + + This overrides the caster's attack for three attacks (attack delay + 500, range 10) to instead randomly move around any entity that the + player tries to attack by up to 11 paces. + + + create_item : entity * int * int -> () + | entity * string * int -> () + create_item(entity, item, count) gives the `entity' `count' + instances of `item'. As usual, `item' can be either a string name + or an item ID. + + + aggravate : entity * int * entity -> () + aggravate (victim, mode, target) causes the `victim' to + mode = 0: attack `target' + mode = 1: become universally permanently aggressive + mode = 2: both of the above + + + injure : entity * entity * int * int -> () + injure(attacker, defender, hp, sp) causes damage to the defender + from the attacker (possibly killing, giving XP etc.), unless the + defender is immortal (NPC) or a PC on a non-PvP map. + + + emote : entity * int -> () + Issues the specified emotion to the specified entity. + + + set_script_variable : entity * string * int -> () + Sets a script variable to the specified value + + + set_hair_colour : entity * int -> () + Sets the hair colour of the specified entity to the specified + value (must be a PC). + + + set_hair_style : entity * int -> () + Adjusts the hair style of a PC. + + + drop_item : location * (int | string) * int * int -> () + drop_item(place, "name", count, duration) drops `count' items + (where count may be zero) of name "name" at `place'. The items + vanish again after `duration'. + + + drop_item_for : location * (int | string) + * int * int * entity * int -> () + drop_item_for(place, obj, count, duration, owner, delay) works + like drop_item(place, obj, count, duration), except that the item + can only be picked up by `owner' for the next `delay' + milliseconds, modulo pickup rules (spousal pickup, out-of-range). + + + gain_experience : entity * int * int * int -> () + gain_experience(player, base_xp, job_xp, reason) gives expereince + points to `player'. If reason is 0, they are handed out normally, + but if reason is 1, then those experience points are NOT added to + the pool of experience points that healers can draw from `player'. + + +Script API updates: +------------------- +Two new script API functions are available: + + * getspellinvocation : string -> string + Looks up a spell by spell ID and returns the spell's invocation. + * getanchorinvocation : string -> string + Looks up a teleport anchor by anchor ID and returns the invocation. + + +Syntax Reference: +----------------- + +SPELLCONF ::= (GLOBAL | ANCHOR | SPELL | PROCEDURE | ';')* (* The ';' are only for decorative purposes *) + + VALUE ::= + | + | + | + | (* one of {N, S, E, W, NE, SE, NW, SW} *) + + EXPR ::= (VALUE) + | (AREA) + | (EXPR) '+' (EXPR) + | (EXPR) '*' (EXPR) + | (EXPR) '-' (EXPR) + | (EXPR) '/' (EXPR) + | (EXPR) '%' (EXPR) + | (EXPR) '<' (EXPR) + | (EXPR) '>' (EXPR) + | (EXPR) '<>' (EXPR) + | (EXPR) '=' (EXPR) + | (EXPR) '!=' (EXPR) + | (EXPR) '==' (EXPR) + | (EXPR) '<=' (EXPR) + | (EXPR) '>=' (EXPR) + | (EXPR) '&&' (EXPR) + | (EXPR) '||' (EXPR) + | (EXPR) '|' (EXPR) + | (EXPR) '^' (EXPR) (* XOR *) + | (EXPR) '&' (EXPR) (* binary AND *) + | (EXPR) '<<' (EXPR) + | (EXPR) '>>' (EXPR) + | '(' ((EXPR) /* ',') ')' + | + | '(' (EXPR) ')' + + INVOCATION ::= (* used for convenience *) + + LOCATION ::= '@' '(' (EXPR) ',' (EXPR) ',' (EXPR) ')' (* *) + + AREA ::= (LOCATION) + | (LOCATION) '@' '+' '(' (EXPR) ',' (EXPR) ')' (* width and height around location, passable only *) + | (LOCATION) 'towards' (EXPR) ':' '(' (EXPR) ',' (EXPR) ')' (* towards dir: Bar-shaped, width and depth; only NSEW supported *) + + ITEMS ::= '[' (ITEMC) /+ ',' ']' + + ITEMC ::= (ITEM) + | '*' (ITEM) + + ITEM ::= + | + +* global + + GLOBAL ::= 'CONST'? '=' (EXPR) ';' + + Available globals: + - min_casttime : int (* min # of ticks for each spell (BEFORE scaling according to battle.conf) *) + - obscure : int (* obscure percentage *) + +* teleport-anchor + + ANCHOR ::= 'TELEPORT-ANCHOR' ':' (INVOCATION) '=' (EXPR) + + For example, + + TELEPORT-ANCHOR t : "tulimshar" = @("map_3-1.gat", 44, 70) + + creates a teleport anchor with name `t' and invocation `tulimshar' at the speicfied location. + +* spell + + SPELL ::= spellmod 'SPELL' (ARG)? ':' (INVOCATION) '=' (SPELLDEF) + + SPELLMOD ::= ('SILENT' | 'LOCAL')* + + (silent: invocation silently dropped. local: `location' variable doesn't change after spell init.) + + ARG ::= '(' ':' (ARGTYPE) ')" + + ARGTYPE ::= 'PC' (* yields a pc, or self *) + | 'STRING' + + SPELLDEF ::= ((SPELLBODY) /* '|') + | 'LET' (DECL)* 'IN' ((SPELLBODY) /* '|') + + SPELLBODY ::= (SPELLGUARD) '=>' (SPELLBODY) + | 'EFFECT' (EFFECT) ('ATTRIGGER' (EFFECT))? ('ATEND' (EFFECT))? + + (* `ATTRIGGER' is executed as a separate effect if the spell is `triggered'. If the trigger has a target, it is denoted + * in the variable `target'. + * `ATEND' is invoked for normal termination, i.e., after the spell is over or has been dispelled. `ATEND' is NOT used + * for the triggered effect; instead, spawn a separate spell in ATTRIGGER if that is desired. *) + + DECL ::= '=' (EXPR) ';' + + SPELLGUARD ::= (PREREQ) + | (SPELLGUARD) 'or' (SPELLGUARD) + | '(' (SPELLGUARD) /+ ',' ')' + + PREREQ ::= 'REQUIRE' (EXPR) + | 'CATALYSTS' (ITEMS) + | 'COMPONENTS' (ITEMS) + | 'MANA' (EXPR) + | 'CASTTIME' (EXPR) (* # of ticks until the next spell can be cast *0 + + EFFECT ::= EFFECT ';' EFFECT + | '(' (EFFECT) ')' + | 'SKIP' (* no-op *) + | 'ABORT' (* abort spell. If used in a trigger, no charges will be consumed. *) + | 'END' (* skip to atend *) + | 'BREAK' (* break out of loop *) + | '=' (EXPR) (* interpreted in the current context, eval'ds trictly *) + | 'FOREACH' (SELECTION) 'IN' (AREA) 'DO' (EFFECT) (* randomises the subjects first *) + | 'FOR' '=' (EXPR) 'TO' (EXPR) 'DO' (EFFECT) (* bounds are evaluated when the loop begins, no updates are respected *) + | 'IF' (EXPR) 'THEN' (EFFECT) ('ELSE' (EFFECT))? + | 'WAIT' (EXPR) (* amount in ticks before continuing. *) + | '(' (EXPR) /* ',' ')' (* operation *) + | 'CALL' '(' (EXPR) /* ',' ')' (* procedure call *) + | '{' ... '}' (* script *) + + SELECTION ::= 'PC' + | 'MOB' + | 'ENTITY' (* MOB or PC *) + | 'TARGET' (* like ENTITY, but includes PCs only on PvP maps *) + | 'SPELL' + | 'NPC' + +* procedures + + Procedures may be invoked via `CALL'. They use dynamic scoping. + + PROCEDURE ::= 'PROCEDURE' '(' /* ',' ')' '=' (EFFECT) diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp index e3557dd..97b570e 100644 --- a/src/admin/ladmin.cpp +++ b/src/admin/ladmin.cpp @@ -224,29 +224,6 @@ AString ladmin_log_filename = "log/ladmin.log"_s; // all other values are 'No MSG', then use state 9 please. // 'error_message_#7': message of the code error 6 = Your are Prohibited to log in until %s (packet 0x006a) // -// timeadd -// Adds or substracts time from the validity limit of an account. -// Modifier is done as follows: -// Adjustment value (-1, 1, +1, etc...) -// Modified element: -// a or y: year -// m: month -// j or d: day -// h: hour -// mn: minute -// s: second -// timeadd testname +1m-2mn1s-6y -// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. -// NOTE: You can not modify a unlimited validity limit. -// If you want modify it, you want probably create a limited validity limit. -// So, at first, you must set the validity limit to a date/time. -// -// timeset yyyy/mm/dd [hh:mm:ss] -// Changes the validity limit of an account. -// Default time [hh:mm:ss]: 23:59:59. -// timeset 0 -// Gives an unlimited validity limit (0 = unlimited). -// // unban/unbanish // Unban an account. // Like banset 0. @@ -491,13 +468,6 @@ void display_help(ZString param) PRINTF(" Research by name is not possible with this command.\n"_fmt); PRINTF(" list 10 9999999\n"_fmt); } - else if (command == "itemfrob"_s) - { - PRINTF("itemfrob \n"_fmt); - PRINTF(" Translates item IDs for all accounts.\n"_fmt); - PRINTF(" Any items matching the source item ID will be mapped to the dest-id.\n"_fmt); - PRINTF(" itemfrob 500 700\n"_fmt); - } else if (command == "listban"_s) { PRINTF("listban [start_id [end_id]]\n"_fmt); @@ -569,34 +539,6 @@ void display_help(ZString param) PRINTF(" 'error_message_#7': message of the code error 6\n"_fmt); PRINTF(" = Your are Prohibited to log in until... (packet 0x006a)\n"_fmt); } - else if (command == "timeadd"_s) - { - PRINTF("timeadd \n"_fmt); - PRINTF(" Adds or substracts time from the validity limit of an account.\n"_fmt); - PRINTF(" Modifier is done as follows:\n"_fmt); - PRINTF(" Adjustment value (-1, 1, +1, etc...)\n"_fmt); - PRINTF(" Modified element:\n"_fmt); - PRINTF(" a or y: year\n"_fmt); - PRINTF(" m: month\n"_fmt); - PRINTF(" j or d: day\n"_fmt); - PRINTF(" h: hour\n"_fmt); - PRINTF(" mn: minute\n"_fmt); - PRINTF(" s: second\n"_fmt); - PRINTF(" timeadd testname +1m-2mn1s-6y\n"_fmt); - PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"_fmt); - PRINTF(" and 6 years at the same time.\n"_fmt); - PRINTF("NOTE: You can not modify a unlimited validity limit.\n"_fmt); - PRINTF(" If you want modify it, you want probably create a limited validity limit.\n"_fmt); - PRINTF(" So, at first, you must set the validity limit to a date/time.\n"_fmt); - } - else if (command == "timeadd"_s) - { - PRINTF("timeset yyyy/mm/dd [hh:mm:ss]\n"_fmt); - PRINTF(" Changes the validity limit of an account.\n"_fmt); - PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"_fmt); - PRINTF("timeset 0\n"_fmt); - PRINTF(" Gives an unlimited validity limit (0 = unlimited).\n"_fmt); - } else if (command == "unban"_s) { PRINTF("unban/unbanish \n"_fmt); @@ -648,7 +590,6 @@ void display_help(ZString param) PRINTF(" gm [GM_level] -- Modify the GM level of an account\n"_fmt); PRINTF(" id -- Give the id of an account\n"_fmt); PRINTF(" info -- Display all information of an account\n"_fmt); - PRINTF(" itemfrob -- Map all items from one item ID to another\n"_fmt); PRINTF(" kami -- Sends a broadcast message (in yellow)\n"_fmt); PRINTF(" kamib -- Sends a broadcast message (in blue)\n"_fmt); PRINTF(" list [First_id [Last_id]] -- Display a list of accounts\n"_fmt); @@ -665,10 +606,6 @@ void display_help(ZString param) PRINTF(" search -- Seek accounts\n"_fmt); PRINTF(" sex -- Modify the sex of an account\n"_fmt); PRINTF(" state -- Change the state\n"_fmt); - PRINTF(" timeadd -- Add or substract time from the\n"_fmt); - PRINTF(" example: ta apple +1m-2mn1s-2y validity limit of an account\n"_fmt); - PRINTF(" timeset yyyy/mm/dd [hh:mm:ss] -- Change the validify limit\n"_fmt); - PRINTF(" timeset 0 -- Give a unlimited validity limit\n"_fmt); PRINTF(" unban -- Remove the banishment of an account\n"_fmt); PRINTF(" unblock -- Set state 0 (Account ok) to an account\n"_fmt); PRINTF(" version -- Gives the version of the login-server\n"_fmt); @@ -1264,29 +1201,6 @@ void listaccount(ZString param, int type) list_count = 0; } -//-------------------------------------------------------- -// Sub-function: Frobnicate items -//-------------------------------------------------------- -static -int itemfrob(ZString param) -{ - ItemNameId source_id, dest_id; - - if (!extract(param, record<' '>(&source_id, &dest_id))) - { - PRINTF("You must provide the source and destination item IDs.\n"_fmt); - return 1; - } - - Packet_Fixed<0x7924> fixed_24; - fixed_24.source_item_id = source_id; - fixed_24.dest_item_id = dest_id; - send_fpacket<0x7924, 10>(login_session, fixed_24); - bytes_to_read = 1; // all logging is done to the three main servers - - return 0; -} - //-------------------------------------------- // Sub-function: Asking to modify a memo field //-------------------------------------------- @@ -1556,179 +1470,6 @@ void blockaccount(ZString param) changestatesub(name, 5, "-"_s); // state 5, no error message } -//--------------------------------------------------------------------- -// Sub-function: Add/substract time to the validity limit of an account -//--------------------------------------------------------------------- -static -void timeaddaccount(ZString param) -{ - AccountName name; - HumanTimeDiff modif {}; - - if (!qsplit(param, &name, &modif)) - { - PRINTF("Please input an account name and a modifier.\n"_fmt); - PRINTF(" : timeadd testname +1m-2mn1s-6y\n"_fmt); - PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"_fmt); - PRINTF(" and 6 years at the same time.\n"_fmt); - LADMIN_LOG("Incomplete parameters to modify a limit time ('timeadd' command).\n"_fmt); - return; - } - if (name.is_print()) - { - return; - } - - if (!modif) - { - PRINTF("Please give an adjustment with this command:\n"_fmt); - PRINTF(" Adjustment value (-1, 1, +1, etc...)\n"_fmt); - PRINTF(" Modified element:\n"_fmt); - PRINTF(" a or y: year\n"_fmt); - PRINTF(" m: month\n"_fmt); - PRINTF(" j or d: day\n"_fmt); - PRINTF(" h: hour\n"_fmt); - PRINTF(" mn: minute\n"_fmt); - PRINTF(" s: second\n"_fmt); - PRINTF(" timeadd testname +1m-2mn1s-6y\n"_fmt); - PRINTF(" this example adds 1 month and 1 second, and substracts 2 minutes\n"_fmt); - PRINTF(" and 6 years at the same time.\n"_fmt); - LADMIN_LOG("No adjustment isn't an adjustment ('timeadd' command).\n"_fmt); - return; - } - - LADMIN_LOG("Request to login-server to modify a time limit.\n"_fmt); - - Packet_Fixed<0x7950> fixed_50; - fixed_50.account_name = name; - fixed_50.valid_add = modif; - send_fpacket<0x7950, 38>(login_session, fixed_50); - bytes_to_read = 1; -} - -//------------------------------------------------- -// Sub-function: Set a validity limit of an account -//------------------------------------------------- -static -void timesetaccount(ZString param) -{ - AccountName name; - XString date; - XString time_; - int year, month, day, hour, minute, second; - - year = month = day = hour = minute = second = 0; - - // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - TimeT connect_until_time = TimeT(); - struct tm tmtime = connect_until_time; // initialize - - if (!qsplit(param, &name, &date, &time_) - && !qsplit(param, &name, &date)) - { - PRINTF("Please input an account name, a date and a hour.\n"_fmt); - PRINTF(": timeset yyyy/mm/dd [hh:mm:ss]\n"_fmt); - PRINTF(" timeset 0 (0 = unlimited)\n"_fmt); - PRINTF(" Default time [hh:mm:ss]: 23:59:59.\n"_fmt); - LADMIN_LOG("Incomplete parameters to set a limit time ('timeset' command).\n"_fmt); - return; - } - if (!name.is_print()) - return; - - if (!time_) - time_ = "23:59:59"_s; - - if (date != "0"_s - && ((!extract(date, record<'/'>(&year, &month, &day)) - && !extract(date, record<'-'>(&year, &month, &day)) - && !extract(date, record<'.'>(&year, &month, &day))) - || !extract(time_, record<':'>(&hour, &minute, &second)))) - { - PRINTF("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"_fmt); - LADMIN_LOG("Invalid format for the date/time ('timeset' command).\n"_fmt); - return; - } - - if (date == "0"_s) - { - connect_until_time = TimeT(); - } - else - { - if (year < 70) - { - year = year + 100; - } - if (year >= 1900) - { - year = year - 1900; - } - if (month < 1 || month > 12) - { - PRINTF("Please give a correct value for the month (from 1 to 12).\n"_fmt); - LADMIN_LOG("Invalid month for the date ('timeset' command).\n"_fmt); - return; - } - month = month - 1; - if (day < 1 || day > 31) - { - PRINTF("Please give a correct value for the day (from 1 to 31).\n"_fmt); - LADMIN_LOG("Invalid day for the date ('timeset' command).\n"_fmt); - return; - } - if (((month == 3 || month == 5 || month == 8 || month == 10) - && day > 30) ||(month == 1 && day > 29)) - { - PRINTF("Please give a correct value for a day of this month (%d).\n"_fmt, - month); - LADMIN_LOG("Invalid day for this month ('timeset' command).\n"_fmt); - return; - } - if (hour < 0 || hour > 23) - { - PRINTF("Please give a correct value for the hour (from 0 to 23).\n"_fmt); - LADMIN_LOG("Invalid hour for the time ('timeset' command).\n"_fmt); - return; - } - if (minute < 0 || minute > 59) - { - PRINTF("Please give a correct value for the minutes (from 0 to 59).\n"_fmt); - LADMIN_LOG("Invalid minute for the time ('timeset' command).\n"_fmt); - return; - } - if (second < 0 || second > 59) - { - PRINTF("Please give a correct value for the seconds (from 0 to 59).\n"_fmt); - LADMIN_LOG("Invalid second for the time ('timeset' command).\n"_fmt); - return; - } - tmtime.tm_year = year; - tmtime.tm_mon = month; - tmtime.tm_mday = day; - tmtime.tm_hour = hour; - tmtime.tm_min = minute; - tmtime.tm_sec = second; - tmtime.tm_isdst = -1; // -1: no winter/summer time modification - connect_until_time = tmtime; - if (connect_until_time.error()) - { - PRINTF("Invalid date.\n"_fmt); - PRINTF("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"_fmt); - LADMIN_LOG("Invalid date. ('timeset' command).\n"_fmt); - return; - } - } - - LADMIN_LOG("Request to login-server to set a time limit.\n"_fmt); - - Packet_Fixed<0x7948> fixed_48; - fixed_48.account_name = name; - fixed_48.valid_until = connect_until_time; - send_fpacket<0x7948, 30>(login_session, fixed_48); - bytes_to_read = 1; -} - //------------------------------------------------------------------------------ // Sub-function: Asking to displaying information about an account (by its name) //------------------------------------------------------------------------------ @@ -1878,8 +1619,6 @@ void prompt(void) infoaccount(wrap(static_cast(atoi(parameters.c_str())))); else if (command == "kami"_s) sendbroadcast(parameters); // flag for normal - else if (command == "itemfrob"_s) - itemfrob(parameters); // 0: to list all else if (command == "list"_s) listaccount(parameters, 0); // 0: to list all else if (command == "listban"_s) @@ -1902,10 +1641,6 @@ void prompt(void) changesex(parameters); else if (command == "state"_s) changestate(parameters); - else if (command == "timeadd"_s) - timeaddaccount(parameters); - else if (command == "timeset"_s) - timesetaccount(parameters); else if (command == "unban"_s) unbanaccount(parameters); else if (command == "unblock"_s) @@ -1990,17 +1725,6 @@ void parse_fromlogin(Session *s) break; } - case 0x7925: // Itemfrob-OK - { - Packet_Fixed<0x7925> fixed; - rv = recv_fpacket<0x7925, 2>(s, fixed); - if (rv != RecvResult::Complete) - break; - - bytes_to_read = 0; - break; - } - case 0x7921: // Displaying of the list of accounts { std::vector> repeat; @@ -2491,47 +2215,6 @@ void parse_fromlogin(Session *s) break; } - case 0x7949: // answer of an account validity limit set - { - Packet_Fixed<0x7949> fixed; - rv = recv_fpacket<0x7949, 34>(s, fixed); - if (rv != RecvResult::Complete) - break; - - AccountId account_id = fixed.account_id; - AccountName name = fixed.account_name; - if (!account_id) - { - PRINTF("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt, - name); - LADMIN_LOG("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt, - name); - } - else - { - TimeT timestamp = fixed.valid_until; - if (!timestamp) - { - PRINTF("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n"_fmt, - name, account_id); - LADMIN_LOG("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n"_fmt, - name, account_id); - } - else - { - timestamp_seconds_buffer tmpstr; - stamp_time(tmpstr, ×tamp); - PRINTF("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt, - name, account_id, tmpstr); - LADMIN_LOG("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt, - name, account_id, - tmpstr); - } - } - bytes_to_read = 0; - break; - } - case 0x794b: // answer of an account ban set { Packet_Fixed<0x794b> fixed; @@ -2636,50 +2319,6 @@ void parse_fromlogin(Session *s) break; } - case 0x7951: // answer of an account validity limit changing - { - Packet_Fixed<0x7951> fixed; - rv = recv_fpacket<0x7951, 34>(s, fixed); - if (rv != RecvResult::Complete) - break; - - AccountId account_id = fixed.account_id; - AccountName name = fixed.account_name; - if (!account_id) - { - PRINTF("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt, - name); - LADMIN_LOG("Account [%s] validity limit changing failed. Account doesn't exist.\n"_fmt, - name); - } - else - { - TimeT timestamp = fixed.valid_until; - if (!timestamp) - { - PRINTF("Validity limit of the account [%s][id: %d] unchanged.\n"_fmt, - name, account_id); - PRINTF("The account have an unlimited validity limit or\n"_fmt); - PRINTF("the changing is impossible with the proposed adjustments.\n"_fmt); - LADMIN_LOG("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments.\n"_fmt, - name, account_id); - } - else - { - timestamp_seconds_buffer tmpstr; - stamp_time(tmpstr, ×tamp); - PRINTF("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt, - name, account_id, - tmpstr); - LADMIN_LOG("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n"_fmt, - name, account_id, - tmpstr); - } - } - bytes_to_read = 0; - break; - } - case 0x7953: // answer of a request about informations of an account (by account name/id) { Packet_Head<0x7953> head; @@ -2700,7 +2339,6 @@ void parse_fromlogin(Session *s) timestamp_milliseconds_buffer lastlogin = head.last_login_string; VString<15> last_ip_ = head.ip_string; AccountEmail email = head.email; - TimeT connect_until_time = head.connect_until; TimeT ban_until_time = head.ban_until; AString& memo = repeat; if (!account_id) @@ -2794,17 +2432,9 @@ void parse_fromlogin(Session *s) connections); PRINTF(" Last connection at: %s (ip: %s)\n"_fmt, lastlogin, last_ip_); - if (!connect_until_time) { PRINTF(" Validity limit: unlimited.\n"_fmt); } - else - { - timestamp_seconds_buffer tmpstr; - stamp_time(tmpstr, &connect_until_time); - PRINTF(" Validity limit: until %s.\n"_fmt, - tmpstr); - } PRINTF(" Memo: '%s'\n"_fmt, memo); } } diff --git a/src/char/char.cpp b/src/char/char.cpp index 0b166c2..8ce2165 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -145,7 +145,6 @@ struct char_session_data : SessionData SEX sex; unsigned short packet_tmw_version; AccountEmail email; - TimeT connect_until_time; }; void SessionDeleter::operator()(SessionData *sd) @@ -162,7 +161,6 @@ struct AuthFifoEntry int delflag; SEX sex; unsigned short packet_tmw_version; - TimeT connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) }; static std::array auth_fifo; @@ -1287,7 +1285,6 @@ void parse_tologin(Session *ls) sd->email = stringish(fixed.email); if (!e_mail_check(sd->email)) sd->email = DEFAULT_EMAIL; - sd->connect_until_time = fixed.connect_until; // send characters to player mmo_char_send006b(s2, sd); } @@ -1326,7 +1323,6 @@ void parse_tologin(Session *ls) sd->email = fixed.email; if (!e_mail_check(sd->email)) sd->email = DEFAULT_EMAIL; - sd->connect_until_time = fixed.connect_until; break; } } @@ -1484,64 +1480,6 @@ void parse_tologin(Session *ls) break; } - case 0x7924: - { // [Fate] Itemfrob package: forwarded from login-server - Packet_Fixed<0x7924> fixed; - rv = recv_fpacket<0x7924, 10>(ls, fixed); - if (rv != RecvResult::Complete) - break; - - ItemNameId source_id = fixed.source_item_id; - ItemNameId dest_id = fixed.dest_item_id; - - Packet_Fixed<0x2afa> fixed_fa; - fixed_fa.source_item_id = source_id; - fixed_fa.dest_item_id = dest_id; - - // forward package to map servers - for (Session *ss : iter_map_sessions()) - { - send_fpacket<0x2afa, 10>(ss, fixed_fa); - } - - for (CharPair& cp : char_keys) - { - CharKey *k = &cp.key; - CharData& cd = *cp.data.get(); - CharData *c = &cd; - Borrowed s = account2storage(k->account_id); - int changes = 0; -#define FIX(v) if (v == source_id) {v = dest_id; ++changes; } - for (IOff0 j : IOff0::iter()) - { - FIX(c->inventory[j].nameid); - } - // used to FIX cart, but it's no longer supported - // FIX(c->weapon); - FIX(c->shield); - FIX(c->head_top); - FIX(c->head_mid); - FIX(c->head_bottom); - - { - for (SOff0 j : SOff0::iter()) - { - FIX(s->storage_[j].nameid); - } - } -#undef FIX - if (changes) - CHAR_LOG("itemfrob(%d -> %d): `%s'(%d, account %d): changed %d times\n"_fmt, - source_id, dest_id, k->name, k->char_id, - k->account_id, changes); - - } - - mmo_char_sync(); - inter_storage_save(); - break; - } - // Account deletion notification (from login-server) case 0x2730: { @@ -1739,22 +1677,6 @@ void parse_frommap(Session *ms) { switch (packet_id) { - // request from map-server to reload GM accounts. Transmission to login-server (by Yor) - case 0x2af7: - { - Packet_Fixed<0x2af7> fixed; - rv = recv_fpacket<0x2af7, 2>(ms, fixed); - if (rv != RecvResult::Complete) - break; - - if (login_session) - { // don't send request if no login-server - Packet_Fixed<0x2709> fixed_09; - send_fpacket<0x2709, 2>(login_session, fixed_09); - } - break; - } - // Receiving map names list from the map-server case 0x2afa: { @@ -1880,7 +1802,6 @@ void parse_frommap(Session *ms) Packet_Payload<0x2afd> payload_fd; // not file descriptor payload_fd.account_id = account_id; payload_fd.login_id2 = afi.login_id2; - payload_fd.connect_until = afi.connect_until_time; cd->sex = afi.sex; payload_fd.packet_tmw_version = afi.packet_tmw_version; FPRINTF(stderr, @@ -1986,7 +1907,6 @@ void parse_frommap(Session *ms) auth_fifo_iter->login_id1 = fixed.login_id1; auth_fifo_iter->login_id2 = fixed.login_id2; auth_fifo_iter->delflag = 2; - auth_fifo_iter->connect_until_time = TimeT(); // unlimited/unknown time by default (not display in map-server) auth_fifo_iter->ip = fixed.ip; auth_fifo_iter++; @@ -2025,7 +1945,6 @@ void parse_frommap(Session *ms) auth_fifo_iter->char_id = fixed.char_id; auth_fifo_iter->delflag = 0; auth_fifo_iter->sex = fixed.sex; - auth_fifo_iter->connect_until_time = TimeT(); // unlimited/unknown time by default (not display in map-server) auth_fifo_iter->ip = fixed.client_ip; // default, if not found in the loop @@ -2417,7 +2336,6 @@ void handle_x0066(Session *s, struct char_session_data *sd, uint8_t rfifob_2, IP auth_fifo_iter->login_id2 = sd->login_id2; auth_fifo_iter->delflag = 0; auth_fifo_iter->sex = sd->sex; - auth_fifo_iter->connect_until_time = sd->connect_until_time; auth_fifo_iter->ip = s->client_ip; auth_fifo_iter->packet_tmw_version = sd->packet_tmw_version; auth_fifo_iter++; @@ -2486,7 +2404,6 @@ void parse_char(Session *s) s->session_data = make_unique(); sd = static_cast(s->session_data.get()); sd->email = stringish("no mail"_s); // put here a mail without '@' to refuse deletion if we don't receive the e-mail - sd->connect_until_time = TimeT(); // unknow or illimited (not displaying on map-server) } sd->account_id = account_id; sd->login_id1 = fixed.login_id1; @@ -2665,9 +2582,6 @@ void parse_char(Session *s) s->set_eof(); return; } - AccountEmail email = fixed.email; - if (!e_mail_check(email)) - email = DEFAULT_EMAIL; { { diff --git a/src/login/login.cpp b/src/login/login.cpp index fa528ba..526eb5d 100644 --- a/src/login/login.cpp +++ b/src/login/login.cpp @@ -177,8 +177,6 @@ GmLevel min_level_to_connect = GmLevel::from(0_u32); // minimum level of player static int add_to_unlimited_account = 0; // Give possibility or not to adjust (ladmin command: timeadd) the time of an unlimited account. static -int start_limited_time = -1; // Starting additional sec from now for the limited time at creation of accounts (-1: unlimited time, 0 or more: additional sec from now) -static int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) DIAG_PUSH(); @@ -216,7 +214,6 @@ struct AuthData AccountEmail email; // e-mail (by default: a@a.com) timestamp_seconds_buffer error_message; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a) TimeT ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - TimeT connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) IP4Address last_ip; // save of last IP of connection VString<254> memo; // a memo field int account_reg2_num; @@ -482,7 +479,7 @@ AString mmo_auth_tostr(const AuthData *p) p->state, p->email, p->error_message, - p->connect_until_time, + TimeT(), p->last_ip, p->memo, p->ban_until_time); @@ -499,6 +496,7 @@ AString mmo_auth_tostr(const AuthData *p) static bool extract(XString line, AuthData *ad) { + TimeT unused_connect_until_time; std::vector vars; VString<1> sex; VString<15> ip; @@ -513,7 +511,7 @@ bool extract(XString line, AuthData *ad) &ad->state, &ad->email, &ad->error_message, - &ad->connect_until_time, + &unused_connect_until_time, &ip, &ad->memo, &ad->ban_until_time, @@ -797,17 +795,6 @@ AccountId mmo_auth_new(struct mmo_account *account, SEX sex, AccountEmail email) ad.error_message = stringish("-"_s); ad.ban_until_time = TimeT(); - if (start_limited_time < 0) - ad.connect_until_time = TimeT(); // unlimited - else - { - // limited time - TimeT timestamp = static_cast(TimeT::now()) + start_limited_time; - // there used to be a silly overflow check here, but it wasn't - // correct, and we don't support time-limited accounts. - ad.connect_until_time = timestamp; - } - ad.last_ip = IP4Address(); ad.memo = "!"_s; ad.account_reg2_num = 0; @@ -901,14 +888,6 @@ int mmo_auth(struct mmo_account *account, Session *s) } } - if (ad->connect_until_time - && ad->connect_until_time < TimeT::now()) - { - LOGIN_LOG("Connection refused (account: %s, expired ID, ip: %s)\n"_fmt, - account->userid, ip); - return 2; // 2 = This ID is expired - } - LOGIN_LOG("Authentification accepted (account: %s (id: %d), ip: %s)\n"_fmt, account->userid, ad->account_id, ip); } @@ -997,22 +976,6 @@ void parse_fromchar(Session *s) switch (packet_id) { - // request from map-server via char-server to reload GM accounts (by Yor). - case 0x2709: - { - Packet_Fixed<0x2709> fixed; - rv = recv_fpacket<0x2709, 2>(s, fixed); - if (rv != RecvResult::Complete) - break; - - LOGIN_LOG("Char-server '%s': Request to re-load GM configuration file (ip: %s).\n"_fmt, - server[id].name, ip); - read_gm_account(); - // send GM accounts to all char-servers - send_GM_accounts(); - break; - } - case 0x2712: // request from char-server to authentify an account { Packet_Fixed<0x2712> fixed; @@ -1059,7 +1022,6 @@ void parse_fromchar(Session *s) fixed_13.account_id = acc; fixed_13.invalid = 0; fixed_13.email = ad.email; - fixed_13.connect_until = ad.connect_until_time; send_fpacket<0x2713, 51>(s, fixed_13); break; @@ -1118,7 +1080,6 @@ void parse_fromchar(Session *s) Packet_Fixed<0x2717> fixed_17; fixed_17.account_id = account_id; fixed_17.email = ad.email; - fixed_17.connect_until = ad.connect_until_time; send_fpacket<0x2717, 50>(s, fixed_17); if (rv != RecvResult::Complete) @@ -1720,23 +1681,6 @@ void parse_admin(Session *s) break; } - case 0x7924: - { // [Fate] Itemfrob package: change item IDs - Packet_Fixed<0x7924> fixed; - rv = recv_fpacket<0x7924, 10>(s, fixed); - if (rv != RecvResult::Complete) - break; - - for (Session *ss : iter_char_sessions()) - { - send_fpacket<0x7924, 10>(ss, fixed); - } - - Packet_Fixed<0x7925> fixed_25; - send_fpacket<0x7925, 2>(s, fixed_25); - break; - } - case 0x7930: // Request for an account creation { Packet_Fixed<0x7930> fixed; @@ -2332,48 +2276,6 @@ void parse_admin(Session *s) break; } - case 0x7948: // Request to change the validity limit (timestamp) (absolute value) - { - Packet_Fixed<0x7948> fixed; - rv = recv_fpacket<0x7948, 30>(s, fixed); - if (rv != RecvResult::Complete) - break; - - Packet_Fixed<0x7949> fixed_49; - { - fixed_49.account_id = AccountId(); - AccountName account_name = stringish(fixed.account_name.to_print()); - TimeT timestamp = fixed.valid_until; - timestamp_seconds_buffer tmpstr = stringish("unlimited"_s); - if (timestamp) - stamp_time(tmpstr, ×tamp); - AuthData *ad = search_account(account_name); - if (ad) - { - fixed_49.account_name = ad->userid; - LOGIN_LOG("'ladmin': Change of a validity limit (account: %s, new validity: %lld (%s), ip: %s)\n"_fmt, - ad->userid, - timestamp, - tmpstr, - ip); - ad->connect_until_time = timestamp; - fixed_49.account_id = ad->account_id; - } - else - { - fixed_49.account_name = account_name; - LOGIN_LOG("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %lld (%s), ip: %s)\n"_fmt, - account_name, - timestamp, - tmpstr, - ip); - } - fixed_49.valid_until = timestamp; - } - send_fpacket<0x7949, 34>(s, fixed_49); - break; - } - case 0x794a: // Request to change the final date of a banishment (timestamp) (absolute value) { Packet_Fixed<0x794a> fixed; @@ -2585,99 +2487,6 @@ void parse_admin(Session *s) break; } - case 0x7950: // Request to change the validity limite (timestamp) (relative change) - { - Packet_Fixed<0x7950> fixed; - rv = recv_fpacket<0x7950, 38>(s, fixed); - if (rv != RecvResult::Complete) - break; - - Packet_Fixed<0x7951> fixed_51; - { - fixed_51.account_id = AccountId(); - AccountName account_name = stringish(fixed.account_name.to_print()); - AuthData *ad = search_account(account_name); - if (ad) - { - fixed_51.account_id = ad->account_id; - fixed_51.account_name = ad->userid; - if (add_to_unlimited_account == 0 && !ad->connect_until_time) - { - LOGIN_LOG("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)\n"_fmt, - ad->userid, ip); - fixed_51.valid_until = TimeT(); - } - else - { - TimeT now = TimeT::now(); - TimeT timestamp = ad->connect_until_time; - if (!timestamp || timestamp < now) - timestamp = now; - struct tm tmtime = timestamp; - HumanTimeDiff v_diff = fixed.valid_add; - tmtime.tm_year += v_diff.year; - tmtime.tm_mon += v_diff.month; - tmtime.tm_mday += v_diff.day; - tmtime.tm_hour += v_diff.hour; - tmtime.tm_min += v_diff.minute; - tmtime.tm_sec += v_diff.second; - timestamp = tmtime; - if (timestamp.okay()) - { - timestamp_seconds_buffer tmpstr = stringish("unlimited"_s); - timestamp_seconds_buffer tmpstr2 = stringish("unlimited"_s); - if (ad->connect_until_time) - stamp_time(tmpstr, &ad->connect_until_time); - if (timestamp) - stamp_time(tmpstr2, ×tamp); - LOGIN_LOG("'ladmin': Adjustment of a validity limit (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %lld (%s), ip: %s)\n"_fmt, - ad->userid, - ad->connect_until_time, - tmpstr, - v_diff.year, - v_diff.month, - v_diff.day, - v_diff.hour, - v_diff.minute, - v_diff.second, - timestamp, - tmpstr2, - ip); - ad->connect_until_time = timestamp; - fixed_51.valid_until = timestamp; - } - else - { - timestamp_seconds_buffer tmpstr = stringish("unlimited"_s); - if (ad->connect_until_time) - stamp_time(tmpstr, &ad->connect_until_time); - LOGIN_LOG("'ladmin': Impossible to adjust a validity limit (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n"_fmt, - ad->userid, - ad->connect_until_time, - tmpstr, - v_diff.year, - v_diff.month, - v_diff.day, - v_diff.hour, - v_diff.minute, - v_diff.second, - ip); - fixed_51.valid_until = TimeT(); - } - } - } - else - { - fixed_51.account_name = account_name; - LOGIN_LOG("'ladmin': Attempt to adjust the validity limit of an unknown account (account: %s, ip: %s)\n"_fmt, - account_name, ip); - fixed_51.valid_until = TimeT(); - } - } - send_fpacket<0x7951, 34>(s, fixed_51); - break; - } - case 0x7952: // Request about informations of an account (by account name) { Packet_Fixed<0x7952> fixed; @@ -2701,7 +2510,6 @@ void parse_admin(Session *s) head_53.last_login_string = ad->lastlogin; head_53.ip_string = convert_for_printf(ad->last_ip); head_53.email = ad->email; - head_53.connect_until = ad->connect_until_time; head_53.ban_until = ad->ban_until_time; XString repeat_53 = ad->memo; @@ -2747,7 +2555,6 @@ void parse_admin(Session *s) head_53.last_login_string = ad.lastlogin; head_53.ip_string = convert_for_printf(ad.last_ip); head_53.email = ad.email; - head_53.connect_until = ad.connect_until_time; head_53.ban_until = ad.ban_until_time; XString repeat_53 = ad.memo; send_vpacket<0x7953, 150, 1>(s, head_53, repeat_53); @@ -3401,10 +3208,6 @@ bool login_config(XString w1, ZString w2) { add_to_unlimited_account = config_switch(w2); } - else if (w1 == "start_limited_time"_s) - { - start_limited_time = atoi(w2.c_str()); - } else if (w1 == "check_ip_flag"_s) { check_ip_flag = config_switch(w2); @@ -3626,14 +3429,6 @@ bool display_conf_warnings(void) rv = false; } - if (start_limited_time < -1) - { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time - PRINTF("***WARNING: Invalid value for start_limited_time parameter\n"_fmt); - PRINTF(" -> set to -1 (new accounts are created with unlimited time).\n"_fmt); - start_limited_time = -1; - rv = false; - } - if (check_ip_flag != 0 && check_ip_flag != 1) { // 0: no, 1: yes PRINTF("***WARNING: Invalid value for check_ip_flag parameter\n"_fmt); @@ -3775,13 +3570,9 @@ void save_config_in_log(void) else LOGIN_LOG("- to refuse adjustment (with timeadd ladmin) on an unlimited account. You must use timeset (ladmin command) before.\n"_fmt); - if (start_limited_time < 0) + { LOGIN_LOG("- to create new accounts with an unlimited time.\n"_fmt); - else if (start_limited_time == 0) - LOGIN_LOG("- to create new accounts with a limited time: time of creation.\n"_fmt); - else - LOGIN_LOG("- to create new accounts with a limited time: time of creation + %d second(s).\n"_fmt, - start_limited_time); + } if (check_ip_flag) LOGIN_LOG("- with control of players IP between login-server and char-server.\n"_fmt); diff --git a/src/map/battle.cpp b/src/map/battle.cpp index f852265..88fcc07 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -985,17 +985,6 @@ int battle_stopattack(dumb_ptr bl) return 0; } -// 移動停止 -int battle_stopwalking(dumb_ptr bl, int type) -{ - nullpo_retz(bl); - if (bl->bl_type == BL::MOB) - return mob_stop_walking(bl->is_mob(), type); - else if (bl->bl_type == BL::PC) - return pc_stop_walking(bl->is_player(), type); - return 0; -} - /*========================================== * ダメージ最終計算 *------------------------------------------ diff --git a/src/map/battle.hpp b/src/map/battle.hpp index aa96a72..7733391 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -58,7 +58,6 @@ int battle_heal(dumb_ptr bl, dumb_ptr target, int hp, // 攻撃や移動を止める int battle_stopattack(dumb_ptr bl); -int battle_stopwalking(dumb_ptr bl, int type); // 通常攻撃処理まとめ ATK battle_weapon_attack(dumb_ptr bl, dumb_ptr target, diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp index dbdd401..9a36a60 100644 --- a/src/map/chrif.cpp +++ b/src/map/chrif.cpp @@ -912,126 +912,6 @@ int chrif_recvgmaccounts(Session *s, const std::vector>& r return 0; } -/*========================================== - * Request to reload GM accounts and their levels: send to char-server by [Yor] - *------------------------------------------ - */ -int chrif_reloadGMdb(void) -{ - if (!char_session) - return -1; - - Packet_Fixed<0x2af7> fixed_f7; - send_fpacket<0x2af7, 2>(char_session, fixed_f7); - - return 0; -} - -/*======================================== - * Map item IDs - *---------------------------------------- - */ - -static -void ladmin_itemfrob_fix_item(ItemNameId source, ItemNameId dest, Item *item) -{ - if (item && item->nameid == source) - { - item->nameid = dest; - item->equip = EPOS::ZERO; - } -} - -static -void ladmin_itemfrob_c2(dumb_ptr bl, ItemNameId source_id, ItemNameId dest_id) -{ -#define IFIX(v) if (v == source_id) {v = dest_id; } -#define FIX(item) ladmin_itemfrob_fix_item(source_id, dest_id, &item) - - if (!bl) - return; - - switch (bl->bl_type) - { - case BL::PC: - { - dumb_ptr pc = bl->is_player(); - - for (IOff0 j : IOff0::iter()) - IFIX(pc->status.inventory[j].nameid); - // cart is no longer supported - // IFIX(pc->status.weapon); - IFIX(pc->status.shield); - IFIX(pc->status.head_top); - IFIX(pc->status.head_mid); - IFIX(pc->status.head_bottom); - - Option> stor_ = account2storage2(pc->status_key.account_id); - if OPTION_IS_SOME(stor, stor_) - { - for (SOff0 j : SOff0::iter()) - FIX(stor->storage_[j]); - } - - for (IOff0 j : IOff0::iter()) - { - P item = TRY_UNWRAP(pc->inventory_data[j], continue); - if (item->nameid == source_id) - { - item->nameid = dest_id; - if (bool(item->equip)) - pc_unequipitem(pc, j, CalcStatus::NOW); - item->nameid = dest_id; - } - } - - break; - } - - case BL::MOB: - { - dumb_ptr mob = bl->is_mob(); - for (Item& itm : mob->lootitemv) - FIX(itm); - break; - } - - case BL::ITEM: - { - dumb_ptr item = bl->is_item(); - FIX(item->item_data); - break; - } - } -#undef FIX -#undef IFIX -} - -static -void ladmin_itemfrob_c(dumb_ptr bl, ItemNameId source_id, ItemNameId dest_id) -{ - ladmin_itemfrob_c2(bl, source_id, dest_id); -} - -static -void ladmin_itemfrob(Session *, const Packet_Fixed<0x2afa>& fixed) -{ - ItemNameId source_id = fixed.source_item_id; - ItemNameId dest_id = fixed.dest_item_id; - dumb_ptr bl = map_get_first_session(); - - // flooritems - map_foreachobject(std::bind(ladmin_itemfrob_c, ph::_1, source_id, dest_id), - BL::NUL /* any object */); - - // player characters (and, hopefully, mobs) - while (bl->bl_next) - { - ladmin_itemfrob_c2(bl, source_id, dest_id); - bl = bl->bl_next; - } -} - static void chrif_delete(Session *s) { @@ -1066,16 +946,6 @@ void chrif_parse(Session *s) chrif_connectack(s, fixed); break; } - case 0x2afa: - { - Packet_Fixed<0x2afa> fixed; - rv = recv_fpacket<0x2afa, 10>(s, fixed); - if (rv != RecvResult::Complete) - break; - - ladmin_itemfrob(s, fixed); - break; - } case 0x2afb: { Packet_Fixed<0x2afb> fixed; @@ -1095,12 +965,11 @@ void chrif_parse(Session *s) AccountId id = payload.account_id; int login_id2 = payload.login_id2; - TimeT connect_until_time = payload.connect_until; short tmw_version = payload.packet_tmw_version; CharKey st_key = payload.char_key; CharData st_data = payload.char_data; pc_authok(id, login_id2, - connect_until_time, tmw_version, + tmw_version, &st_key, &st_data); break; } diff --git a/src/map/chrif.hpp b/src/map/chrif.hpp index 4bd00aa..59fb541 100644 --- a/src/map/chrif.hpp +++ b/src/map/chrif.hpp @@ -47,7 +47,6 @@ void chrif_changeemail(AccountId id, AccountEmail actual_email, AccountEmail new void chrif_char_ask_name(AccountId id, CharName character_name, short operation_type, HumanTimeDiff modif); int chrif_saveaccountreg2(dumb_ptr sd); -int chrif_reloadGMdb(void); int chrif_send_divorce(CharId char_id); void do_init_chrif(void); diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 430d928..edca352 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -622,37 +622,6 @@ int clif_clearchar(dumb_ptr bl, BeingRemoveWhy type) return 0; } -static -void clif_clearchar_delay_sub(TimerData *, tick_t, - dumb_ptr bl, BeingRemoveWhy type) -{ - clif_clearchar(bl, type); - MapBlockLock::freeblock(bl); -} - -int clif_clearchar_delay(tick_t tick, - dumb_ptr bl, BeingRemoveWhy type) -{ - dumb_ptr tmpbl; - tmpbl.new_(); - - // yikes! - tmpbl->bl_next = bl->bl_next; - tmpbl->bl_prev = bl->bl_prev; - tmpbl->bl_id = bl->bl_id; - tmpbl->bl_m = bl->bl_m; - tmpbl->bl_x = bl->bl_x; - tmpbl->bl_y = bl->bl_y; - tmpbl->bl_type = bl->bl_type; - - Timer(tick, - std::bind(clif_clearchar_delay_sub, ph::_1, ph::_2, - tmpbl, type) - ).detach(); - - return 0; -} - /*========================================== * *------------------------------------------ @@ -2683,43 +2652,6 @@ void clif_mobinsight(dumb_ptr bl, dumb_ptr md) } } -/*========================================== - * - *------------------------------------------ - */ -int clif_skillinfo(dumb_ptr sd, SkillID skillid, int type, - int range) -{ - nullpo_retz(sd); - - Session *s = sd->sess; - if (!sd->status.skill[skillid].lv) - return 0; - Packet_Fixed<0x0147> fixed_147; - fixed_147.info.skill_id = skillid; - if (type < 0) - fixed_147.info.type_or_inf = skill_get_inf(skillid); - else - fixed_147.info.type_or_inf = type; - fixed_147.info.flags = SkillFlags::ZERO; - fixed_147.info.level = sd->status.skill[skillid].lv; - fixed_147.info.sp = skill_get_sp(skillid, sd->status.skill[skillid].lv); - if (range < 0) - { - range = skill_get_range(skillid, sd->status.skill[skillid].lv); - if (range < 0) - range = battle_get_range(sd) - (range + 1); - fixed_147.info.range = range; - } - else - fixed_147.info.range = range; - fixed_147.info.unused = ""_s; - fixed_147.info.can_raise = sd->status.skill[skillid].lv < skill_get_max_raise(skillid); - send_fpacket<0x0147, 39>(s, fixed_147); - - return 0; -} - /*========================================== * スキルリストを送信する *------------------------------------------ @@ -3200,21 +3132,6 @@ int clif_movetoattack(dumb_ptr sd, dumb_ptr bl) return 0; } -/*========================================== - * MVPエフェクト - *------------------------------------------ - */ -int clif_mvp_effect(dumb_ptr sd) -{ - nullpo_retz(sd); - - Packet_Fixed<0x010c> fixed_10c; - fixed_10c.block_id = sd->bl_id; - Buffer buf = create_fpacket<0x010c, 6>(fixed_10c); - clif_send(buf, sd, SendWho::AREA); - return 0; -} - /*========================================== * エモーション *------------------------------------------ @@ -3837,25 +3754,6 @@ RecvResult clif_parse_Emotion(Session *s, dumb_ptr sd) return rv; } -/*========================================== - * - *------------------------------------------ - */ -static -RecvResult clif_parse_HowManyConnections(Session *s, dumb_ptr) -{ - Packet_Fixed<0x00c1> fixed; - RecvResult rv = recv_fpacket<0x00c1, 2>(s, fixed); - if (rv != RecvResult::Complete) - return rv; - - Packet_Fixed<0x00c2> fixed_c2; - fixed_c2.users = map_getusers(); - send_fpacket<0x00c2, 6>(s, fixed_c2); - - return rv; -} - /*========================================== * *------------------------------------------ @@ -4755,7 +4653,7 @@ RecvResult clif_parse_RemovePartyMember(Session *s, dumb_ptr s *------------------------------------------ */ static -RecvResult clif_parse_PartyChangeOpt0(Session *s, dumb_ptr sd) +RecvResult clif_parse_PartyChangeOption(Session *s, dumb_ptr sd) { Packet_Fixed<0x0102> fixed; RecvResult rv = recv_fpacket<0x0102, 6>(s, fixed); @@ -5000,7 +4898,7 @@ func_table clif_parse_func_table[0x0220] = {0, 5, nullptr, }, // 0x00be {1000, 3, clif_parse_Emotion, }, // 0x00bf {0, 7, nullptr, }, // 0x00c0 - {0, 2, clif_parse_HowManyConnections, }, // 0x00c1 + {0, 2, nullptr, }, // 0x00c1 {0, 6, nullptr, }, // 0x00c2 {0, 8, nullptr, }, // 0x00c3 {0, 6, nullptr, }, // 0x00c4 @@ -5065,7 +4963,7 @@ func_table clif_parse_func_table[0x0220] = {0, 10, clif_parse_ReplyPartyInvite, }, // 0x00ff {0, 2, clif_parse_LeaveParty, }, // 0x0100 {0, 6, nullptr, }, // 0x0101 - {0, 6, clif_parse_PartyChangeOpt0, }, // 0x0102 + {0, 6, clif_parse_PartyChangeOption, }, // 0x0102 {0, 30, clif_parse_RemovePartyMember, }, // 0x0103 {0, 79, nullptr, }, // 0x0104 {0, 31, nullptr, }, // 0x0105 diff --git a/src/map/clif.hpp b/src/map/clif.hpp index 24f4b80..2242949 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -52,7 +52,6 @@ int clif_charselectok(BlockId); int clif_dropflooritem(dumb_ptr); int clif_clearflooritem(dumb_ptr, Session *); int clif_clearchar(dumb_ptr, BeingRemoveWhy); // area or fd -int clif_clearchar_delay(tick_t, dumb_ptr, BeingRemoveWhy); void clif_clearchar_id(BlockId, BeingRemoveWhy, Session *); int clif_spawnpc(dumb_ptr); //area int clif_spawnnpc(dumb_ptr); // area @@ -131,8 +130,6 @@ void clif_pcoutsight(dumb_ptr, dumb_ptr); void clif_mobinsight(dumb_ptr, dumb_ptr); void clif_moboutsight(dumb_ptr, dumb_ptr); -int clif_skillinfo(dumb_ptr sd, SkillID skillid, int type, - int range); void clif_skillinfoblock(dumb_ptr sd); int clif_skillup(dumb_ptr sd, SkillID skill_num); @@ -152,8 +149,6 @@ void clif_wis_end(Session *s, int flag); void clif_itemlist(dumb_ptr sd); void clif_equiplist(dumb_ptr sd); -int clif_mvp_effect(dumb_ptr sd); - int clif_movetoattack(dumb_ptr sd, dumb_ptr bl); // party diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 90ac917..cd80cdf 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -199,27 +199,6 @@ int npc_delete(dumb_ptr nd) return 0; } -void npc_timer_event(NpcEvent eventname) -{ - P ev = TRY_UNWRAP(ev_db.search(eventname), - { - PRINTF("npc_event: event not found [%s]\n"_fmt, - eventname); - return; - }); - - dumb_ptr nd; - - if ((nd = ev->nd) == nullptr) - { - PRINTF("npc_event: event not found [%s]\n"_fmt, - eventname); - return; - } - - run_script(ScriptPointer(borrow(*nd->scr.script), ev->pos), nd->bl_id, nd->bl_id); -} - /*========================================== * 全てのNPCのOn*イベント実行 *------------------------------------------ diff --git a/src/map/npc.hpp b/src/map/npc.hpp index cb42dbd..e62faf2 100644 --- a/src/map/npc.hpp +++ b/src/map/npc.hpp @@ -44,7 +44,6 @@ constexpr Species INVISIBLE_CLASS = wrap(32767); int npc_event_dequeue(dumb_ptr sd); int npc_event(dumb_ptr sd, NpcEvent npcname, int); -void npc_timer_event(NpcEvent eventname); // Added by RoVeRT int npc_command(dumb_ptr sd, NpcName npcname, XString command); int npc_touch_areanpc(dumb_ptr, Borrowed, int, int); int npc_click(dumb_ptr, BlockId); diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 95bdb8d..468bc2d 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -606,7 +606,7 @@ int pc_isequip(dumb_ptr sd, IOff0 n) * char鯖から送られてきたステータスを設定 *------------------------------------------ */ -int pc_authok(AccountId id, int login_id2, TimeT connect_until_time, +int pc_authok(AccountId id, int login_id2, short tmw_version, const CharKey *st_key, const CharData *st_data) { dumb_ptr sd = nullptr; @@ -760,15 +760,6 @@ int pc_authok(AccountId id, int login_id2, TimeT connect_until_time, sd->packet_flood_reset_due = TimeT(); sd->packet_flood_in = 0; - // message of the limited time of the account - if (connect_until_time) - { - timestamp_seconds_buffer buffer; - stamp_time(buffer, &connect_until_time); - AString tmpstr = STRPRINTF("Your account time limit is: %s"_fmt, buffer); - - clif_wis_message(sd->sess, wisp_server_name, tmpstr); - } pc_calcstatus(sd, 1); return 0; @@ -2530,72 +2521,6 @@ void pc_touch_all_relevant_npcs(dumb_ptr sd) sd->areanpc_id = BlockId(); } -/*========================================== - * - *------------------------------------------ - */ -int pc_movepos(dumb_ptr sd, int dst_x, int dst_y) -{ - int moveblock; - int dx, dy; - - struct walkpath_data wpd; - - nullpo_retz(sd); - - if (path_search(&wpd, sd->bl_m, sd->bl_x, sd->bl_y, dst_x, dst_y, 0)) - return 1; - - sd->dir = sd->head_dir = map_calc_dir(sd, dst_x, dst_y); - - dx = dst_x - sd->bl_x; - dy = dst_y - sd->bl_y; - - moveblock = (sd->bl_x / BLOCK_SIZE != dst_x / BLOCK_SIZE - || sd->bl_y / BLOCK_SIZE != dst_y / BLOCK_SIZE); - - map_foreachinmovearea(std::bind(clif_pcoutsight, ph::_1, sd), - sd->bl_m, - sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE, - sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE, - dx, dy, - BL::NUL); - - if (moveblock) - map_delblock(sd); - sd->bl_x = dst_x; - sd->bl_y = dst_y; - if (moveblock) - map_addblock(sd); - - map_foreachinmovearea(std::bind(clif_pcinsight, ph::_1, sd), - sd->bl_m, - sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE, - sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE, - -dx, -dy, - BL::NUL); - - if (sd->status.party_id) - { // パーティのHP情報通知検査 - Option p = party_search(sd->status.party_id); - if (p.is_some()) - { - int flag = 0; - map_foreachinmovearea(std::bind(party_send_hp_check, ph::_1, sd->status.party_id, &flag), - sd->bl_m, - sd->bl_x - AREA_SIZE, sd->bl_y - AREA_SIZE, - sd->bl_x + AREA_SIZE, sd->bl_y + AREA_SIZE, - -dx, -dy, - BL::PC); - if (flag) - sd->party_hp = -1; - } - } - - pc_touch_all_relevant_npcs(sd); - return 0; -} - // // 武器戦闘 // @@ -3145,94 +3070,6 @@ int pc_skillup(dumb_ptr sd, SkillID skill_num) return 0; } -/*========================================== - * /resetlvl - *------------------------------------------ - */ -int pc_resetlvl(dumb_ptr sd, int type) -{ - nullpo_retz(sd); - - for (SkillID i : erange(SkillID(1), MAX_SKILL)) - { - sd->status.skill[i].lv = 0; - } - - if (type == 1) - { - sd->status.skill_point = 0; - sd->status.base_level = 1; - sd->status.job_level = 1; - sd->status.base_exp = 0; - sd->status.job_exp = 0; - sd->status.option = Opt0::ZERO; - - for (ATTR attr : ATTRs) - sd->status.attrs[attr] = 1; - } - - if (type == 2) - { - sd->status.skill_point = 0; - sd->status.base_level = 1; - sd->status.job_level = 1; - sd->status.base_exp = 0; - sd->status.job_exp = 0; - } - if (type == 3) - { - sd->status.base_level = 1; - sd->status.base_exp = 0; - } - if (type == 4) - { - sd->status.job_level = 1; - sd->status.job_exp = 0; - } - - clif_updatestatus(sd, SP::STATUSPOINT); - clif_updatestatus(sd, SP::STR); - clif_updatestatus(sd, SP::AGI); - clif_updatestatus(sd, SP::VIT); - clif_updatestatus(sd, SP::INT); - clif_updatestatus(sd, SP::DEX); - clif_updatestatus(sd, SP::LUK); - clif_updatestatus(sd, SP::BASELEVEL); - clif_updatestatus(sd, SP::JOBLEVEL); - clif_updatestatus(sd, SP::STATUSPOINT); - clif_updatestatus(sd, SP::NEXTBASEEXP); - clif_updatestatus(sd, SP::NEXTJOBEXP); - clif_updatestatus(sd, SP::SKILLPOINT); - - clif_updatestatus(sd, SP::USTR); // Updates needed stat points - Valaris - clif_updatestatus(sd, SP::UAGI); - clif_updatestatus(sd, SP::UVIT); - clif_updatestatus(sd, SP::UINT); - clif_updatestatus(sd, SP::UDEX); - clif_updatestatus(sd, SP::ULUK); // End Addition - - for (EQUIP i : EQUIPs) - { - // unequip items that can't be equipped by base 1 [Valaris] - IOff0 *idx = &sd->equip_index_maybe[i]; - if ((*idx).ok()) - { - if (!pc_isequip(sd, *idx)) - { - pc_unequipitem(sd, *idx, CalcStatus::LATER); - *idx = IOff0::from(-1); - } - } - } - - clif_skillinfoblock(sd); - pc_calcstatus(sd, 0); - - MAP_LOG_STATS(sd, "STATRESET"_fmt); - - return 0; -} - /*========================================== * /resetstate *------------------------------------------ @@ -3942,21 +3779,6 @@ int pc_changelook(dumb_ptr sd, LOOK type, int val) return 0; } -/*========================================== - * 付属品(鷹,ペコ,カート)設定 - *------------------------------------------ - */ -int pc_setoption(dumb_ptr sd, Opt0 type) -{ - nullpo_retz(sd); - - sd->status.option = type; - clif_changeoption(sd); - pc_calcstatus(sd, 0); - - return 0; -} - /*========================================== * script用変数の値を読む *------------------------------------------ diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 9795443..c889e44 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -77,7 +77,7 @@ int pc_counttargeted(dumb_ptr sd, dumb_ptr src, int pc_setrestartvalue(dumb_ptr sd, int type); void pc_makesavestatus(dumb_ptr); int pc_setnewpc(dumb_ptr, AccountId, CharId, int, uint32_t /*tick_t*/, SEX); -int pc_authok(AccountId, int, TimeT, short tmw_version, const CharKey *, const CharData *); +int pc_authok(AccountId, int, short tmw_version, const CharKey *, const CharData *); int pc_authfail(AccountId accid); EPOS pc_equippoint(dumb_ptr sd, IOff0 n); @@ -87,7 +87,6 @@ IOff0 pc_checkequip(dumb_ptr sd, EPOS pos); int pc_walktoxy(dumb_ptr, int, int); int pc_stop_walking(dumb_ptr, int); -int pc_movepos(dumb_ptr, int, int); int pc_setpos(dumb_ptr, MapName, int, int, BeingRemoveWhy); void pc_setsavepoint(dumb_ptr, MapName, int, int); int pc_randomwarp(dumb_ptr sd, BeingRemoveWhy type); @@ -127,7 +126,6 @@ int pc_need_status_point(dumb_ptr, SP); int pc_statusup(dumb_ptr, SP); int pc_statusup2(dumb_ptr, SP, int); int pc_skillup(dumb_ptr, SkillID); -int pc_resetlvl(dumb_ptr, int type); int pc_resetstate(dumb_ptr); int pc_resetskill(dumb_ptr); int pc_equipitem(dumb_ptr, IOff0, EPOS); @@ -139,7 +137,6 @@ int pc_damage(dumb_ptr, dumb_ptr, int); int pc_heal(dumb_ptr, int, int); int pc_itemheal(dumb_ptr sd, int hp, int sp); int pc_percentheal(dumb_ptr sd, int, int); -int pc_setoption(dumb_ptr, Opt0); int pc_changelook(dumb_ptr, LOOK, int); int pc_readparam(dumb_ptr, SP); diff --git a/src/mmo/skill.t.hpp b/src/mmo/skill.t.hpp index d0e3926..21e4059 100644 --- a/src/mmo/skill.t.hpp +++ b/src/mmo/skill.t.hpp @@ -77,6 +77,9 @@ enum class SkillID : uint16_t // TODO: Remove these! NEGATIVE = 0xffff, ZERO = 0x0000, + // this is probably the remains of the 'basic' skill, + // which has since been partially split into emote, trade, and party, + // but the confusion is caused by the fact that it also covered attacks. ONE = 0x0001, // Basic skills. diff --git a/tools/protocol.py b/tools/protocol.py index 3c33b2c..5a91555 100755 --- a/tools/protocol.py +++ b/tools/protocol.py @@ -27,6 +27,33 @@ import itertools import os from pipes import quote from posixpath import relpath +from weakref import ref as wr + +try: + unicode +except NameError: + unicode = str + +## For various reasons this is all one file, so let's navigate with a +## +## Table of Contents +## +## TOC_HEAD +## TOC_TYPES +## TOC_EVENT +## TOC_CHAN +## TOC_DATA +## TOC_GENTYPE +## TOC_CLIENT +## TOC_LOGINCHAR +## TOC_CHARMAP +## TOC_INTERMAP +## TOC_MISC +## TOC_LOGINADMIN +## TOC_NEW +## TOC_DRAWING +## TOC_MAIN +## # The following code should be relatively easy to understand, but please # keep your sanity fastened and your arms and legs inside at all times. @@ -34,6 +61,9 @@ from posixpath import relpath # important note: all numbers in this file will make a lot more sense in # decimal, but they're written in hex. + +# TOC_HEAD + generated = '// This is a generated file, edit %s instead\n' % __file__ copyright = '''// {filename} - {description} @@ -69,9 +99,9 @@ class OpenWrite(object): if ty is not None: return frag = ''' - if cmp {0}.tmp {0}.old + if cmp -s {0}.tmp {0}.old then - echo Unchanged: {0} + : echo Unchanged: {0} rm {0}.tmp mv {0}.old {0} else @@ -82,6 +112,194 @@ class OpenWrite(object): '''.format(quote(self.filename)) os.system(frag) + +# TOC_ + +def gvq(s): + return u'"%s"' % s.replace(u'"', u'\\"') + +def gva(d): + if d: + return u' [%s]' % u', '.join(u'%s=%s' % (ak, gvq(av)) for (ak, av) in sorted(d.items())) + return u'' + +class Attributes(object): + __slots__ = (u'_attributes') + + def __init__(self): + self._attributes = {} + + def __getitem__(self, k): + assert isinstance(k, unicode) + return self._attributes[k] + + def __setitem__(self, k, v): + assert isinstance(k, unicode) + assert isinstance(v, unicode) + self._attributes[k] = v + + def __delitem__(self, k): + assert isinstance(k, unicode) + del self._attributes[k] + + def merge(self, *others): + for other in others: + assert self.__class__ == other.__class__ + # if an attribute is present on multiple inputs, prefer + # the last one. This is not necessarily what you want, though. + self._attributes.update(other._attributes) + +class Graph(Attributes): + __slots__ = (u'default_vertex', u'default_edge', u'_vertices', u'_edges', u'_vertex_lookup') + + def __init__(self): + Attributes.__init__(self) + self.default_vertex = Attributes() + self.default_edge = Attributes() + self._vertices = set() + self._edges = {} + self._vertex_lookup = {} + + def vertex(self, name, insert=True): + assert isinstance(name, unicode) + vert = self._vertex_lookup.get(name) + if insert and vert is None: + vert = Vertex(name) + self._vertex_lookup[name] = vert + self._vertices.add(vert) + return vert + + def _fix_vertex(self, v, insert=True): + if isinstance(v, Vertex): + return v + return self.vertex(v, insert) + + def edge(self, v1, v2, insert=True): + v1 = self._fix_vertex(v1, insert) + if v1 is None: + return None + v2 = self._fix_vertex(v2, insert) + if v2 is None: + return None + ek = (v1, v2) + edge = self._edges.get(ek) + if insert and edge is None: + edge = Edge(v1, v2) + v1._post.add(wr(v2)) + v2._pre.add(wr(v1)) + self._edges[ek] = edge + return edge + + def del_edge(self, v1, v2): + edge = self.edge(v1, v2, False) + if edge is None: + return + v1 = edge._from + v2 = edge._to + ek = (v1, v2) + v1._post.remove(wr(v2)) + v2._pre.remove(wr(v1)) + del self._edges[ek] + + def del_vertex(self, v): + v = self._fix_vertex(v, False) + if v is None: + return + pre = list(v._pre) + for vp in pre: + vp = vp() + self.del_edge(vp, v) + post = list(v._post) + for vp in post: + vp = vp() + self.del_edge(v, vp) + del self._vertex_lookup[v._key] + self._vertices.remove(v) + + def splice_out_vertex(self, v): + v = self._fix_vertex(v, False) + if v is None: + return + self.del_edge(v, v) + for vp in v._pre: + vp = vp() + ep = self.edge(vp, v, False) + for vn in v._post: + vn = vn() + en = self.edge(v, vn, False) + self.edge(vp, vn).merge(ep, en) + self.del_vertex(v) + + def dot(self, out, close): + if close: + with out: + self.dot(out, False) + return + + def p(*args): + for x in args: + out.write(unicode(x)) + out.write(u'\n') + p(u'digraph') + p(u'{') + for ak, av in sorted(self._attributes.items()): + p(u' ', ak, u'=', gvq(av), u';') + for ak, av in sorted(self.default_vertex._attributes.items()): + p(u' node [', ak, u'=', gvq(av), u'];') + for ak, av in sorted(self.default_edge._attributes.items()): + p(u' edge [', ak, u'=', gvq(av), u'];') + for n in sorted(self._vertices): + p(u' ', n) + for _, e in sorted(self._edges.items()): + p(u' ', e) + p(u'}') + + def dot_str(self): + from io import StringIO + out = StringIO() + self.dot(out, False) + return out.getvalue() + + def dot_file(self, name): + with open(name, u'w') as f: + self.dot(f, False) + + def preview(self, block): + from subprocess import Popen, PIPE + proc = Popen([u'dot', u'-Txlib', u'/dev/stdin'], stdin=PIPE, universal_newlines=True) + self.dot(proc.stdin, True) + if block: + proc.wait() + +class Vertex(Attributes): + __slots__ = (u'_key', u'_post', u'_pre', u'__weakref__') + + def __init__(self, key): + Attributes.__init__(self) + self._key = key + self._post = set() + self._pre = set() + + def __str__(self): + return u'%s%s;' % (gvq(self._key), gva(self._attributes)) + + def __lt__(self, other): + return self._key < other._key + +class Edge(Attributes): + __slots__ = (u'_from', u'_to') + + def __init__(self, f, t): + Attributes.__init__(self) + self._from = f + self._to = t + + def __str__(self): + return u'%s -> %s%s;' % (gvq(self._from._key), gvq(self._to._key), gva(self._attributes)) + + +# TOC_TYPES + class LowType(object): __slots__ = ('includes') @@ -95,6 +313,10 @@ class NativeType(LowType): self.name = name self.includes = frozenset({include}) if include else frozenset() + def __repr__(self): + return 'NativeType(%r)' % (self.name) + + def a_tag(self): return self.name @@ -105,6 +327,10 @@ class NetworkType(LowType): self.name = name self.includes = frozenset({include}) if include else frozenset() + def __repr__(self): + return 'NetworkType(%r)' % (self.name) + + def e_tag(self): return self.name @@ -129,6 +355,10 @@ class NeutralType(Type): return 'NeutralType(%r)' % (self.name) + def __repr__(self): + return 'NeutralType(%r)' % (self.name) + + def native_tag(self): return self.name @@ -180,6 +410,10 @@ class StringType(Type): return 'StringType(%r)' % (self.native) + def __repr__(self): + return 'StringType(%r)' % self.native + + def native_tag(self): return self.native.a_tag() @@ -202,6 +436,10 @@ class ProvidedType(Type): return 'ProvidedType(%r, %r)' % (self.native, self.network) + def __repr__(self): + return 'ProvidedType(native=%r, network=%r)' % (self.native, self.network) + + def native_tag(self): return self.native.a_tag() @@ -221,6 +459,10 @@ class EnumType(Type): return 'EnumType(%r, %r)' % (self.native, self.under) + def __repr__(self): + return 'EnumType(%r, %r)' % (self.native, self.under) + + def native_tag(self): return self.native.a_tag() @@ -270,6 +512,10 @@ class WrappedType(Type): return 'WrappedType(%r, %r)' % (self.native, self.under) + def __repr__(self): + return 'WrappedType(%r, %r)' % (self.native, self.under) + + def native_tag(self): return self.native.a_tag() @@ -351,6 +597,10 @@ class StructType(Type): return '' % (self.name, len(self.fields)) + def __repr__(self): + return '' % (self.id, self.name, self.size, self.ctor, len(self.fields)) + + def native_tag(self): return self.name @@ -451,6 +701,10 @@ class PartialStructType(Type): return '' % (self.native, len(self.body)) + def __repr__(self): + return '' % (self.native, len(self.body)) + + def native_tag(self): return self.native.a_tag() @@ -554,6 +808,47 @@ class InvArrayType(Type): return 'NetArray<%s, %s>' % (self.element.network_tag(), self.count) +# TOC_EVENT +# special event origins +class SpecialEventOrigin(object): + __slots__ = ('name') + + def __init__(self, name): + self.name = name + + def __repr__(self): + return self.name + +ADMIN = SpecialEventOrigin('ADMIN') +BOOT = SpecialEventOrigin('BOOT') +FINISH = SpecialEventOrigin('FINISH') +GM = SpecialEventOrigin('GM') +HUMAN = SpecialEventOrigin('HUMAN') +IDLE = SpecialEventOrigin('IDLE') +MAGIC = SpecialEventOrigin('MAGIC') +NOTHING = SpecialEventOrigin('NOTHING') +OTHER = SpecialEventOrigin('OTHER') +PRETTY = SpecialEventOrigin('PRETTY') +SCRIPT = SpecialEventOrigin('SCRIPT') +TIMER = SpecialEventOrigin('TIMER') + +def event_name(p): + if isinstance(p, SpecialEventOrigin): + return p.name + if isinstance(p, int): + return 'packet 0x%04x' % p + assert False, 'Unknown event: %r' % p + +def event_link(p): + if isinstance(p, SpecialEventOrigin): + return p.name + if isinstance(p, int): + return '[[Packet 0x%04x]]' % p + assert False, 'Unknown event: %r' % p + + +# TOC_CHAN + class Include(object): __slots__ = ('path', '_types') @@ -565,6 +860,19 @@ class Include(object): return '' % (self.path, len(self._types)) + def testcase(self, outdir): + basename = os.path.basename(self.path.strip('<">')) + root = os.path.splitext(basename)[0] + filename = 'include_%s_test.cpp' % root.replace('.', '_') + desc = 'testsuite for protocol includes' + poison = relpath('src/poison.hpp', outdir) + with OpenWrite(os.path.join(outdir, filename)) as f: + f.write(self.pp(0)) + f.write(copyright.format(filename=filename, description=desc)) + f.write('\n') + f.write('#include "%s"\n\nnamespace tmwa\n{\n' % poison) + + def pp(self, n): return '#%*sinclude %s\n' % (n, '', self.path) @@ -583,15 +891,103 @@ class Include(object): self._types.append(ty) return ty +class BasePacket(object): + __slots__ = ('id', 'name', 'define', 'pre', 'post', 'xpost', 'desc') + + def __init__(self, **kwargs): + for s in BasePacket.__slots__: + setattr(self, s, kwargs[s]) + del kwargs[s] + assert not kwargs, 'Unknown kwargs: %s' % repr(kwargs) + + def base_repr_fragment(self): + return ', '.join('%s=%r' % (s, getattr(self, s)) for s in BasePacket.__slots__) + + def comment_doc(self): + id = self.id + name = self.name + define = self.define + desc = self.desc + pre = self.pre + post = self.post + + comment = 'Packet 0x%04x: "%s"\n' % (id, name) + if define: + comment += 'define: ' + define + '\n' + if True: + prestr = ', '.join(event_name(x) for x in pre) or 'none' + poststr = ', '.join(event_name(x) for x in post) or 'none' + comment += 'pre: ' + prestr + '\n' + comment += 'post: ' + poststr + '\n' + comment += desc + comment = ''.join('// ' + c + '\n' if c else '//\n' for c in comment.split('\n')) + return comment + + def wiki_doc(self): + # TODO do markdown magic + id = self.id + name = self.name + desc = self.desc + pre = self.pre + post = self.post + xpost = self.xpost + + wiki = 'Packet 0x%04x: "%s"\n\n' % (id, name) + if True: + prestr = ', '.join(event_link(x) for x in pre) or 'none' + poststr = ', '.join(event_link(x) for x in fix_sort(post + xpost)) or 'none' + wiki += 'pre: ' + prestr + '\n\n' + wiki += 'post: ' + poststr + '\n\n' + wiki += desc + wiki += '\n\n' + wiki += '![](packets-around-0x%04x.png)\n' % id + return wiki + + def pre_set(self, d, n=float('inf'), accum=None): + if accum is None: + accum = set() + if self.id in accum: + return accum + accum.add(self.id) + if not n: + return accum + for p in self.pre: + # ignore specials + if isinstance(p, SpecialEventOrigin): + continue + # ignore weak links + if self.id not in d[p].post: + continue + d[p].pre_set(d, n - 1 + (len([z for z in self.pre if isinstance(z, SpecialEventOrigin) or self.id in d[z].post]) == 1), accum) + return accum + + def post_set(self, d, n=float('inf'), accum=None): + if accum is None: + accum = set() + if self.id in accum: + return accum + accum.add(self.id) + if not n: + return accum + for p in self.post: + # ignore specials + if isinstance(p, SpecialEventOrigin): + continue + # weak links are in xpost + + d[p].post_set(d, n - 1 + (len(self.post) == 1), accum) + return accum -class FixedPacket(object): + +class FixedPacket(BasePacket): __slots__ = ('fixed_struct') - def __init__(self, fixed_struct): + def __init__(self, fixed_struct, **kwargs): + BasePacket.__init__(self, **kwargs) self.fixed_struct = fixed_struct def __repr__(self): - return 'FixedPacket(%r)' % (self.fixed_struct) + return 'FixedPacket(%r, %s)' % (self.fixed_struct, self.base_repr_fragment()) def add_headers_to(self, headers): @@ -602,6 +998,7 @@ class FixedPacket(object): fwd.write('\n') def dump_native(self, f): + f.write(self.comment_doc()) self.fixed_struct.dump_native(f) f.write('\n') @@ -613,15 +1010,16 @@ class FixedPacket(object): self.fixed_struct.dump_convert(f) f.write('\n') -class VarPacket(object): +class VarPacket(BasePacket): __slots__ = ('head_struct', 'repeat_struct') - def __init__(self, head_struct, repeat_struct): + def __init__(self, head_struct, repeat_struct, **kwargs): + BasePacket.__init__(self, **kwargs) self.head_struct = head_struct self.repeat_struct = repeat_struct def __repr__(self): - return 'VarPacket(%r, %r)' % (self.head_struct, self.repeat_struct) + return 'VarPacket(%r, %r, %s)' % (self.head_struct, self.repeat_struct, self.base_repr_fragment()) def add_headers_to(self, headers): @@ -634,6 +1032,7 @@ class VarPacket(object): fwd.write('\n') def dump_native(self, f): + f.write(self.comment_doc()) self.head_struct.dump_native(f) self.repeat_struct.dump_native(f) f.write('\n') @@ -648,40 +1047,76 @@ class VarPacket(object): self.repeat_struct.dump_convert(f) f.write('\n') -def packet(id, name, +def sanitize_line(line, n): + if not line: + return line + m = len(line) - len(line.lstrip(' ')) + assert m >= n, 'not %d: %r' % (n, line) + return line[n:] + +def sanitize_multiline(text): + text = text.strip('\n').rstrip(' ') + assert '\r' not in text + assert '\t' not in text + n = len(text) - len(text.lstrip(' ')) + return '\n'.join(sanitize_line(l, n) for l in text.split('\n')) + +def packet(id, name, define=None, fixed=None, fixed_size=None, payload=None, payload_size=None, head=None, head_size=None, repeat=None, repeat_size=None, option=None, option_size=None, + pre=None, post=None, xpost=None, desc=None, ): assert (fixed is None) <= (fixed_size is None) assert (payload is None) <= (payload_size is None) assert (head is None) <= (head_size is None) assert (repeat is None) <= (repeat_size is None) assert (option is None) <= (option_size is None) + assert (pre is not None) and (post is not None) and desc.strip() + if xpost is None: + xpost = [] + + desc = sanitize_multiline(desc) if fixed is not None: assert not head and not repeat and not option and not payload return FixedPacket( - StructType(id, 'Packet_Fixed<0x%04x>' % id, fixed, fixed_size)) + StructType(id, 'Packet_Fixed<0x%04x>' % id, fixed, fixed_size), + id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc, + ) elif payload is not None: assert not head and not repeat and not option return FixedPacket( - StructType(id, 'Packet_Payload<0x%04x>' % id, payload, payload_size)) + StructType(id, 'Packet_Payload<0x%04x>' % id, payload, payload_size), + id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc, + ) else: assert head if option: + assert not repeat return VarPacket( StructType(id, 'Packet_Head<0x%04x>' % id, head, head_size), - StructType(id, 'Packet_Option<0x%04x>' % id, option, option_size)) + StructType(id, 'Packet_Option<0x%04x>' % id, option, option_size), + id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc, + ) else: assert repeat return VarPacket( StructType(id, 'Packet_Head<0x%04x>' % id, head, head_size), - StructType(id, 'Packet_Repeat<0x%04x>' % id, repeat, repeat_size)) - - + StructType(id, 'Packet_Repeat<0x%04x>' % id, repeat, repeat_size), + id=id, name=name, define=define, pre=pre, post=post, xpost=xpost, desc=desc, + ) + + +# TODO this whole idea is wrong +# in particular, 'any'. +# instead, have just one channel for all packets, but store send/recv/type +# with each packet. +# Then during codegen, emit: +# #if PACKET_LOGIN || PACKET_CHAR || PACKET_MAP || PACKET_ADMIN || PACKET_USER +# (for an 'all' packet) around the send/recv portions separately class Channel(object): __slots__ = ('server', 'client', 'packets') @@ -691,7 +1126,11 @@ class Channel(object): self.packets = [] def __repr__(self): - return '' % (self.server, self.client, len(self.packets)) + return '' % ( + self.server, + self.client, + len(self.packets), + ) def x(self, id, name, **kwargs): @@ -761,8 +1200,11 @@ class Context(object): self._types = [] def __repr__(self): - return '' % ( - len(self._includes), len(self._channels), len(self._types), + return '' % ( + self.outdir, + len(self._includes), + len(self._channels), + len(self._types), ) @@ -910,6 +1352,8 @@ class Context(object): return rv +# TOC_DATA + def build_context(): ## setup @@ -1040,6 +1484,7 @@ def build_context(): interval_t = timer_th.native('interval_t') ## generated types + # TOC_GENTYPE u8 = ctx.provided(uint8_t, Byte) u16 = ctx.provided(uint16_t, Little16) @@ -1388,18 +1833,60 @@ def build_context(): # sized if all of its members are fixed sized. # # The element type of an array shall not be of implicit size. - # The element type of an array *may* be of explicit size, in which + # The element type of an array *may* be of explicit size, in which case + # the layout is like option 1 below (see below for discussion). # # A string is just an array of characters, except that it may be padded # with '\0' bytes even when it is sized. # A map is just an array of two-element structs (key, value) # However, strings and maps have custom classes used to represent them - # on the sender and receiver (earray also has this). + # on the sender and receiver (earray also has this (but maybe shouldn't?)). + # Ytf is tmwa still using fixed-size arrays for this crap anyways? + # It probably should be a map in this case. # # It would probably be a good idea if everybody parsed network input as # padded with '\0's if it is too short, and ignored the extra if it is # too long. However, there are efficiency concerns with this, since we - # don't want to branch everywhere. + # don't want to branch everywhere. But parsing in reverse might work ... + + # possible array layouts: + # 1: + # | array1 size | array1 data | array2 size | array2 data | + # Advantage: linear writing (except final size?). + # Disadvantage: linear reading. + # This is what I originally imagined, but ... + # 2: + # | array1 size/offset | array2 size/offset | array1 data | array2 data | + # Advantage: random-access reading. + # Disadvantage: slightly larger packets, more complicated writing?. + # TODO verify whether you really need offset or not? + # TODO think about array-of-array case, it's complicated. + # | array0 size | array1 size | array 0.0 size | array 0.1 size | array 1.0 size | array 1.1 size | element 0.0.0 | element 0.0.1 | element 0.1.0 | element 0.1.1 | element 1.0.0 | element 1.0.1 | element 1.1.0 | element 1.1.1 | + # data a=(b=[d=[h=0.0.0, i=0.0.1], e=[j=0.1.0, k=0.1.1]], c=[f=[l=1.0.0, m=1.0.1], g=[n=1.1.0, o=1.1.1]]) + # I'm not sure how this can work. If I wrote: + # def write_a(): + # write_head_b() # size + # write_head_c() # size + # write_data_b() + # write_data_c() + # def write_data_b(): + # write_head_d() + # write_head_e() + # write_data_d() + # write_data_e() + # def write_data_c(): + # write_head_f() + # write_head_g() + # write_data_f() + # write_data_g() + # ... that would yield |s0|s1|s0.0|s0.1|d0.0.0|d0.0.1|d0.1.0|d0.1.1|s1.0|s1.1|d1.0.0|d1.0.1|d1.1.0|d1.1.1| + # Can't do random-access writes because you don't know the offset yet. + # I would have to do a bunch of 'write depth n' functions. + # Maybe this was a bad idea. + # And what exactly is the use of random-access reads? We're doing full + # translation for all data anyway. It's probably a bad idea for any + # packet to ever contain information that you don't look at. + login_char = ctx.chan('login', 'char') login_admin = ctx.chan('login', 'admin') @@ -1420,23 +1907,43 @@ def build_context(): ## legacy packets + # TOC_CLIENT # * user - char_user.r(0x0061, 'change password request', + char_user.r(0x0061, 'change password', + define='CMSG_CHAR_PASSWORD_CHANGE', fixed=[ at(0, u16, 'packet id'), at(2, account_pass, 'old pass'), at(26, account_pass, 'new pass'), ], fixed_size=50, + pre=[HUMAN], + post=[0x2740], + desc=''' + Sent by a client to the character server to request a password change. + ''', ) - char_user.s(0x0062, 'change password response', + char_user.s(0x0062, 'change password result', + define='SMSG_CHAR_PASSWORD_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'status'), ], fixed_size=3, - ) - login_user.r(0x0063, 'update host', + pre=[0x2741], + post=[PRETTY], + desc=''' + Sent by the character server with the response of a password change request. + + Status: + 0: The account was not found. + 1: Success. + 2: The old password was incorrect. + 3: The new password was too short. + ''', + ) + login_user.s(0x0063, 'update host notify', + define='SMSG_UPDATE_HOST', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1444,8 +1951,16 @@ def build_context(): head_size=4, repeat=[at(0, u8, 'c')], repeat_size=1, + pre=[0x0064], + post=[IDLE], + desc=''' + This packet gives the client the location of the update server URL, such as http://tmwdata.org/updates/ + + It is only sent if an update host is specified for the server (there is one in the default configuration) and the client identifies as accepting an update host (which all supported clients do). + ''', ) - login_user.r(0x0064, 'login request', + login_user.r(0x0064, 'account login', + define='CMSG_LOGIN_REGISTER', fixed=[ at(0, u16, 'packet id'), at(2, u32, 'unknown'), @@ -1454,8 +1969,16 @@ def build_context(): at(54, version_2, 'version 2 flags'), ], fixed_size=55, + pre=[HUMAN, 0x7531], + post=[0x0063, 0x0069, 0x006a, 0x0081], + desc=''' + Authenticate a client by user/password. + + All clients must now set both defined version 2 flags. + ''', ) - char_user.r(0x0065, 'char-server connection request', + char_user.r(0x0065, 'connect char', + define='CMSG_CHAR_SERVER_CONNECT', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -1465,15 +1988,28 @@ def build_context(): at(16, sex, 'sex'), ], fixed_size=17, + pre=[0x0069, 0x0092, 0x00b3], + post=[0x006b, 0x006c, 0x2712, 0x2716], + desc=''' + Begin connection to the char server, based on keys the login + server gave us. + ''', ) - char_user.r(0x0066, 'select character request', + char_user.r(0x0066, 'select character', + define='CMSG_CHAR_SELECT', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, + pre=[0x006b], + post=[FINISH, 0x0071, 0x0081], + desc=''' + Choose a character to enter the map. + ''', ) - char_user.r(0x0067, 'create character request', + char_user.r(0x0067, 'create character', + define='CMSG_CHAR_CREATE', fixed=[ at(0, u16, 'packet id'), at(2, char_name, 'char name'), @@ -1483,16 +2019,32 @@ def build_context(): at(35, u16, 'hair style'), ], fixed_size=37, + pre=[0x006b], + post=[0x006d, 0x006e], + desc=''' + Create a new character. + ''', ) - char_user.r(0x0068, 'delete character request', + char_user.r(0x0068, 'delete character', + define='CMSG_CHAR_DELETE', fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), - at(6, account_email, 'email'), + at(6, account_email, 'unused email'), ], fixed_size=46, - ) - login_user.r(0x0069, 'login data', + pre=[0x006b], + post=[0x006f, 0x0070, 0x2afe, 0x2b12, 0x3821, 0x3824, 0x3826], + desc=''' + Delete an existing character. + + There is no authentication on the server besides what has + already been performed to create the connection. "Are you sure?" + is solely the client's job. + ''', + ) + login_user.s(0x0069, 'account login success', + define='SMSG_LOGIN_DATA', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1514,16 +2066,44 @@ def build_context(): at(30, u16, 'is new'), ], repeat_size=32, + pre=[0x0064], + post=[0x0065], + desc=''' + Big blob of information available once when you authenticate: + + * dumb session keys + * sex and last login + * list of char server + ''', ) - login_user.s(0x006a, 'login error', + login_user.s(0x006a, 'account login error', + define='SMSG_LOGIN_ERROR', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'error code'), at(3, seconds, 'error message'), ], fixed_size=23, - ) - char_user.s(0x006b, 'update character list', + pre=[0x0064], + post=[PRETTY], + desc=''' + Failure to log in. + + Error codes: + * 0: unregistered id + * 1: incorrect password + * 2: expired id (unused?) + * 3: rejected from server (unused?) + * 4: permanently blocked + * 5: client too old (unused?) + * 6: temporary ban (date in 'error message' field) + * 7: server full + * 8: no message + * 99: id erased + ''', + ) + char_user.s(0x006b, 'connect char success', + define='SMSG_CHAR_LOGIN', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1534,42 +2114,83 @@ def build_context(): at(0, char_select, 'char select'), ], repeat_size=106, + pre=[0x0065, 0x2713], + post=[PRETTY], + xpost=[0x0066, 0x0067, 0x0068], + desc=''' + List account's characters on this server. + ''', ) - char_user.s(0x006c, 'login error', + char_user.s(0x006c, 'connect char error', + define='SMSG_CHAR_LOGIN_ERROR', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, + pre=[0x0065, 0x2713], + post=[PRETTY], + desc=''' + Refuse connection. + + Status: + 0: Overpopulated + 0x42: Auth failed + ''', ) - char_user.s(0x006d, 'create character succeeded', + char_user.s(0x006d, 'create character success', + define='SMSG_CHAR_CREATE_SUCCEEDED', fixed=[ at(0, u16, 'packet id'), at(2, char_select, 'char select'), ], fixed_size=108, + pre=[0x0067], + post=[PRETTY], + desc=''' + Give information about newly-created character. + ''', ) - char_user.s(0x006e, 'create character failed', + char_user.s(0x006e, 'create character error', + define='SMSG_CHAR_CREATE_FAILED', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, + pre=[0x0067], + post=[PRETTY], + desc=''' + Failure to create a new character. + ''', ) - char_user.s(0x006f, 'delete character succeeded', + char_user.s(0x006f, 'delete character success', + define='SMSG_CHAR_DELETE_SUCCEEDED', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[0x0068], + post=[PRETTY], + desc=''' + Character successfully deleted. + ''', ) - char_user.s(0x0070, 'delete character failed', + char_user.s(0x0070, 'delete character error', + define='SMSG_CHAR_DELETE_FAILED', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, + pre=[0x0068], + post=[PRETTY], + desc=''' + Failure to delete character. + ''', ) - char_user.s(0x0071, 'char-map info', + char_user.s(0x0071, 'select character success', + define='SMSG_CHAR_MAP_INFO', fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), @@ -1578,8 +2199,14 @@ def build_context(): at(26, u16, 'port'), ], fixed_size=28, + pre=[0x0066], + post=[0x0072], + desc=''' + Return character location and map server IP. + ''', ) - map_user.r(0x0072, 'map server connect', + map_user.r(0x0072, 'connect map', + define='CMSG_MAP_SERVER_CONNECT', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -1589,8 +2216,15 @@ def build_context(): at(18, sex, 'sex'), ], fixed_size=19, + pre=[0x0071, 0x0092], + post=[0x0081, 0x2afc], + desc=''' + Begin connection to the map server, based on keys the login + server gave us. + ''', ) - map_user.s(0x0073, 'map login succeeded', + map_user.s(0x0073, 'connect map success', + define='SMSG_MAP_LOGIN_SUCCESS', fixed=[ at(0, u16, 'packet id'), at(2, tick32, 'tick'), @@ -1599,8 +2233,14 @@ def build_context(): at(10, u8, 'five2'), ], fixed_size=11, + pre=[0x2afd], + post=[PRETTY, 0x007e], + desc=''' + Successfully auth'd to the map server + ''', ) - map_user.s(0x0078, 'being visibility', + map_user.s(0x0078, 'being appear notify', + define='SMSG_BEING_VISIBLE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -1632,8 +2272,14 @@ def build_context(): at(52, u16, 'level'), ], fixed_size=54, + pre=[BOOT, FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00b8, 0x00b9, 0x00e6, 0x00f7, 0x0143, 0x0146, 0x01d5], + post=[0x0094], + desc=''' + A being is stationary. + ''', ) - map_user.s(0x007b, 'being move', + map_user.s(0x007b, 'being move notify', + define='SMSG_BEING_MOVE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -1666,8 +2312,14 @@ def build_context(): at(58, u16, 'level'), ], fixed_size=60, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f], + post=[0x0094], + desc=''' + A being is moving. + ''', ) - map_user.s(0x007c, 'being spawn', + map_user.s(0x007c, 'being spawn notify', + define='SMSG_BEING_SPAWN', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -1690,50 +2342,95 @@ def build_context(): at(39, u16, 'unknown 11'), ], fixed_size=41, + pre=[BOOT, FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x008c, 0x00b8, 0x00b9, 0x00e6, 0x00f7, 0x0143, 0x0146, 0x01d5], + post=[PRETTY], + desc=''' + A being is created. + ''', ) map_user.r(0x007d, 'map loaded', + define='CMSG_MAP_LOADED', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[0x0091], + post=[SCRIPT, 0x0078, 0x007b, 0x009d, 0x00a4, 0x00b0, 0x00b1, 0x00bd, 0x00fb, 0x0101, 0x010f, 0x0119, 0x013a, 0x0141, 0x019b, 0x01d7, 0x01d8, 0x01d9, 0x01da, 0x01ee, 0x3025, 0x3028], + xpost=[0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x013c, 0x0196, 0x01b1, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Changed map; need info. + ''', ) - map_user.r(0x007e, 'client ping', + map_user.r(0x007e, 'ping', + define='CMSG_MAP_PING', fixed=[ at(0, u16, 'packet id'), at(2, u32, 'client tick'), ], fixed_size=6, + pre=[TIMER, 0x0073], + post=[0x007f], + desc=''' + Request ping. + ''', ) - map_user.s(0x007f, 'server ping', + map_user.s(0x007f, 'pong', + define='SMSG_SERVER_PING', fixed=[ at(0, u16, 'packet id'), at(2, tick32, 'tick'), ], fixed_size=6, + pre=[0x007e], + post=[NOTHING], + desc=''' + Provide ping. + ''', ) - map_user.s(0x0080, 'remove being', + map_user.s(0x0080, 'remove being notify', + define='SMSG_BEING_REMOVE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, being_remove_why, 'type'), ], fixed_size=7, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + A being disappeared. + ''', ) - any_user.s(0x0081, 'connection problem', + any_user.s(0x0081, 'connect foo error', + define='SMSG_CONNECTION_PROBLEM', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'error code'), ], fixed_size=3, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0064, 0x0066, 0x0072, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2afe, 0x2b06, 0x2b0d], + post=[PRETTY], + desc=''' + Failed to connect to some server (multiple meanings). + ''', ) - map_user.r(0x0085, 'change player destination', + map_user.r(0x0085, 'walk', + define='CMSG_PLAYER_CHANGE_DEST', fixed=[ at(0, u16, 'packet id'), at(2, pos1, 'pos'), ], fixed_size=5, + pre=[HUMAN], + post=[0x0080, 0x0087, 0x01d7, 0x01da], + xpost=[SCRIPT, 0x0078, 0x007b, 0x0081, 0x0088, 0x0091, 0x009d, 0x00a0, 0x00a1, 0x00ac, 0x00b0, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Start walking somewhere. + ''', ) - map_user.s(0x0087, 'walk response', + # 0x0086 define='SMSG_BEING_MOVE2', + map_user.s(0x0087, 'walk success', + define='SMSG_WALK_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, tick32, 'tick'), @@ -1741,8 +2438,16 @@ def build_context(): at(11, u8, 'zero'), ], fixed_size=12, + pre=[0x0085, 0x0089], + post=[IDLE], + desc=''' + Confirm that you did walk somewhere. + + No corresponding error! + ''', ) - map_user.s(0x0088, 'player stop', + map_user.s(0x0088, 'stop walking notify', + define='SMSG_PLAYER_STOP', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -1750,16 +2455,36 @@ def build_context(): at(8, u16, 'y'), ], fixed_size=10, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Being stopped walking. + ''', ) map_user.r(0x0089, 'player action', + define='CMSG_PLAYER_CHANGE_ACT', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'target id'), at(6, damage_type, 'action'), ], fixed_size=7, + pre=[HUMAN], + post=[MAGIC, 0x0080, 0x008a, 0x0110, 0x0139], + xpost=[SCRIPT, 0x0078, 0x007b, 0x007c, 0x0081, 0x0087, 0x0088, 0x0091, 0x009e, 0x00a0, 0x00a1, 0x00ac, 0x00af, 0x00b0, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fb, 0x00fd, 0x0101, 0x0106, 0x010f, 0x0119, 0x013a, 0x013b, 0x0141, 0x0196, 0x019b, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x01de, 0x2b01, 0x2b05, 0x3011, 0x3022, 0x3025, 0x3028], + desc=''' + Perform an action. + + Action: + * single attack + * continuous attack + * sit + * stand + * (other actions on return only - special calls) + ''', ) - map_user.s(0x008a, 'being action', + map_user.s(0x008a, 'being action notify', + define='SMSG_BEING_ACTION', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'src id'), @@ -1773,8 +2498,14 @@ def build_context(): at(27, u16, 'damage2'), ], fixed_size=29, - ) - map_user.r(0x008c, 'character chat', + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x009f], + post=[PRETTY], + desc=''' + Somebody performed an action on something/somebody. + ''', + ) + map_user.r(0x008c, 'global chat', + define='CMSG_CHAT_MESSAGE', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1784,8 +2515,15 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, - ) - map_user.s(0x008d, 'being chat', + pre=[HUMAN], + post=[GM, MAGIC, 0x008d, 0x008e], + xpost=[SCRIPT, 0x0078, 0x007b, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00af, 0x00b0, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x2b0e, 0x3003, 0x3011, 0x3022], + desc=''' + Talk to everyone nearby. + ''', + ) + map_user.s(0x008d, 'global chat notify', + define='SMSG_BEING_CHAT', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1796,8 +2534,14 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, - ) - map_user.s(0x008e, 'player chat', + pre=[GM, SCRIPT, 0x008c], + post=[PRETTY], + desc=''' + Somebody is talking (not just a player). + ''', + ) + map_user.s(0x008e, 'global chat result', + define='SMSG_PLAYER_CHAT', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1807,16 +2551,33 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[OTHER, 0x008c], + post=[PRETTY], + desc=''' + You talked. + ''', ) - map_user.r(0x0090, 'chat to npc', + map_user.r(0x0090, 'npc click', + define='CMSG_NPC_TALK', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, u8, 'unused'), ], fixed_size=7, + pre=[HUMAN], + post=[SCRIPT, 0x0080, 0x00b4, 0x00b6, 0x00c4], + xpost=[0x00c0], + desc=''' + Click on an NPC, to invoke its script/shop/message. + + Error if already talking to an NPC or too far. + + No error if clicking on a warp. + ''', ) - map_user.s(0x0091, 'warp player', + map_user.s(0x0091, 'change map notify', + define='SMSG_PLAYER_WARP', fixed=[ at(0, u16, 'packet id'), at(2, map_name, 'map name'), @@ -1824,8 +2585,15 @@ def build_context(): at(20, u16, 'y'), ], fixed_size=22, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[0x007d], + desc=''' + You teleported to a new map (possible the same map), but on the + same server. + ''', ) - map_user.s(0x0092, 'change map server', + map_user.s(0x0092, 'change map server notify', + define='SMSG_CHANGE_MAP_SERVER', fixed=[ at(0, u16, 'packet id'), at(2, map_name, 'map name'), @@ -1835,23 +2603,43 @@ def build_context(): at(26, u16, 'port'), ], fixed_size=28, + pre=[0x2b06], + post=[0x0065, 0x0072], + desc=''' + You teleported to another server. + ''', ) - map_user.r(0x0094, 'request being name', + map_user.r(0x0094, 'get being name', + define='CMSG_NAME_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, + pre=[TIMER, 0x0078, 0x007b, 0x01d8, 0x01d9, 0x01da], + post=[0x0095, 0x0195, 0x020c], + desc=''' + Request a beings name. No reply if wrong type. + + Also send misc other info about players. + ''', ) - map_user.s(0x0095, 'being name response', + map_user.s(0x0095, 'get being name result', + define='SMSG_BEING_NAME_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, char_name, 'char name'), ], fixed_size=30, - ) - map_user.r(0x0096, 'send whisper', + pre=[0x0094], + post=[PRETTY], + desc=''' + Somebody has a name. + ''', + ) + map_user.r(0x0096, 'whisper', + define='CMSG_CHAT_WHISPER', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1862,8 +2650,15 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[HUMAN], + post=[GM, 0x0097, 0x0098, 0x3001], + xpost=[0x2b0e, 0x3003], + desc=''' + Talk to someone privately. + ''', ) map_user.s(0x0097, 'receive whisper', + define='SMSG_WHISPER', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1874,15 +2669,28 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x0096, 0x3801, 0x3803], + post=[PRETTY], + desc=''' + Somebody is talking to you privately. + ''' ) - map_user.s(0x0098, 'whisper status', + map_user.s(0x0098, 'whisper result', + define='SMSG_WHISPER_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'flag'), ], fixed_size=3, - ) - map_user.s(0x009a, 'gm announcement', + pre=[0x0096, 0x3802], + post=[PRETTY], + desc=''' + Did you successfully talk to someone? + ''', + ) + # 0x0099 define='CMSG_ADMIN_ANNOUNCE', + map_user.s(0x009a, 'gm announcement notify', + define='SMSG_GM_CHAT', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -1892,16 +2700,28 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[GM, SCRIPT, 0x3800], + post=[PRETTY], + desc=''' + The GMs are shouting in red. + ''', ) - map_user.r(0x009b, 'change player direction', + map_user.r(0x009b, 'face direction', + define='CMSG_PLAYER_CHANGE_DIR', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'unused'), at(4, u8, 'client dir'), ], fixed_size=5, + pre=[HUMAN], + post=[0x009c], + desc=''' + Look in a different direction, without moving. + ''', ) - map_user.s(0x009c, 'being changed direction', + map_user.s(0x009c, 'face direction notify', + define='SMSG_BEING_CHANGE_DIRECTION', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -1909,8 +2729,14 @@ def build_context(): at(8, u8, 'client dir'), ], fixed_size=9, + pre=[0x009b], + post=[PRETTY], + desc=''' + Somebody looked in a different direction, without moving. + ''', ) - map_user.s(0x009d, 'visible item', + map_user.s(0x009d, 'item visible notify', + define='SMSG_ITEM_VISIBLE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -1923,8 +2749,14 @@ def build_context(): at(16, u8, 'suby'), ], fixed_size=17, + pre=[0x007d, 0x0085], + post=[PRETTY], + desc=''' + An item appeared on the ground. + ''', ) - map_user.s(0x009e, 'dropped item', + map_user.s(0x009e, 'item dropped notify', + define='SMSG_ITEM_DROPPED', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -1937,15 +2769,28 @@ def build_context(): at(15, u16, 'amount'), ], fixed_size=17, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x00a2], + post=[PRETTY], + desc=''' + An item was dropped on the ground. + ''', ) - map_user.r(0x009f, 'pickup item', + map_user.r(0x009f, 'item pickup', + define='CMSG_ITEM_PICKUP', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'object id'), ], fixed_size=6, + pre=[HUMAN], + post=[SCRIPT, 0x0080, 0x008a, 0x0091, 0x00a0, 0x00a1, 0x00b0], + xpost=[0x0078, 0x007b, 0x0081, 0x0088, 0x00ac, 0x00b1, 0x00b4, 0x00b6, 0x00be, 0x00c0, 0x00c4, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Hey, it's just lying around on the floor, I want it. + ''', ) - map_user.s(0x00a0, 'add item to inventory', + map_user.s(0x00a0, 'inventory add notify', + define='SMSG_PLAYER_INVENTORY_ADD', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), @@ -1963,23 +2808,44 @@ def build_context(): at(22, pickup_fail, 'pickup fail'), ], fixed_size=23, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Item appeared in your inventory. + ''', ) - map_user.s(0x00a1, 'item removed', + map_user.s(0x00a1, 'flooritem delete notify', + define='SMSG_ITEM_REMOVE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0085, 0x0089, 0x009f, 0x00a2], + post=[PRETTY], + desc=''' + Item disappeared from the floor. + ''', ) - map_user.r(0x00a2, 'drop an item', + map_user.r(0x00a2, 'drop item', + define='CMSG_PLAYER_INVENTORY_DROP', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), at(4, u16, 'amount'), ], fixed_size=6, + pre=[HUMAN], + post=[0x0080, 0x009e, 0x00ac, 0x00af, 0x01d7], + xpost=[SCRIPT, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00a1, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + This is worthless, just leave it lying on the ground. + + Also used by people who don't understand trades. + ''', ) - map_user.s(0x00a4, 'player equipment', + map_user.s(0x00a4, 'inventory equipment notify', + define='SMSG_PLAYER_EQUIPMENT', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2000,8 +2866,14 @@ def build_context(): at(18, u16, 'card3'), ], repeat_size=20, - ) - map_user.s(0x00a6, 'storage equipment', + pre=[0x007d], + post=[PRETTY], + desc=''' + Complete list of equipment in inventory. + ''', + ) + map_user.s(0x00a6, 'storage equipment notify', + define='SMSG_PLAYER_STORAGE_EQUIP', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2022,16 +2894,29 @@ def build_context(): at(18, u16, 'card3'), ], repeat_size=20, + pre=[GM, SCRIPT, 0x3810], + post=[PRETTY], + desc=''' + Complete list of equipment in storage. + ''', ) - map_user.r(0x00a7, 'use inventory item', + map_user.r(0x00a7, 'use item', + define='CMSG_PLAYER_INVENTORY_USE', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), at(4, u32, 'unused id'), ], fixed_size=8, + pre=[HUMAN], + post=[SCRIPT, 0x0080, 0x00a8, 0x00ac, 0x00af, 0x00b0, 0x01c8, 0x01d7], + xpost=[0x0081, 0x0088, 0x0091, 0x00a0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Use a consumable item in your inventory. + ''', ) - map_user.s(0x00a8, 'item usage response', + map_user.s(0x00a8, 'use item result', + define='SMSG_ITEM_USE_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), @@ -2039,16 +2924,31 @@ def build_context(): at(6, u8, 'ok'), ], fixed_size=7, + pre=[0x00a7], + post=[PRETTY], + desc=''' + You used (or tried to use) an item in your inventory. + + Currently only used in the failure case. + ''', ) - map_user.r(0x00a9, 'equip an item request', + map_user.r(0x00a9, 'equip item', + define='CMSG_PLAYER_EQUIP', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), at(4, epos, 'epos ignored'), ], fixed_size=6, + pre=[HUMAN], + post=[SCRIPT, 0x0080, 0x00aa, 0x00ac, 0x013b, 0x013c, 0x01d7], + xpost=[0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Put on armor or something. + ''', ) - map_user.s(0x00aa, 'item equip ack', + map_user.s(0x00aa, 'equip item result', + define='SMSG_PLAYER_EQUIP', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), @@ -2056,15 +2956,28 @@ def build_context(): at(6, u8, 'ok'), ], fixed_size=7, + pre=[0x00a9], + post=[PRETTY], + desc=''' + Result of trying to wear something. + ''', ) - map_user.r(0x00ab, 'unequip an item', + map_user.r(0x00ab, 'unequip item', + define='CMSG_PLAYER_UNEQUIP', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), ], fixed_size=4, + pre=[HUMAN], + post=[SCRIPT, 0x0080, 0x00ac, 0x01d7], + xpost=[0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Take off armor or something. + ''', ) - map_user.s(0x00ac, 'unequip item ack', + map_user.s(0x00ac, 'unequip item result', + define='SMSG_PLAYER_UNEQUIP', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), @@ -2072,46 +2985,83 @@ def build_context(): at(6, u8, 'ok'), ], fixed_size=7, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Result of trying to unwear something. + ''', ) - map_user.s(0x00af, 'remove item from inventory', + map_user.s(0x00af, 'inventory delete notify', + define='SMSG_PLAYER_INVENTORY_REMOVE', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), at(4, u16, 'amount'), ], fixed_size=6, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x008c, 0x00a2, 0x00a7, 0x00c9, 0x00ef, 0x00f3], + post=[PRETTY], + desc=''' + An item has been deleted from your inventory. + ''', ) - map_user.s(0x00b0, 'player stat update 1', + map_user.s(0x00b0, 'player stat update 1 notify', + define='SMSG_PLAYER_STAT_UPDATE_1', fixed=[ at(0, u16, 'packet id'), at(2, sp, 'sp type'), at(4, u32, 'value'), ], fixed_size=8, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + One of many player stat packets with no real difference. + ''', ) - map_user.s(0x00b1, 'player stat update 2', + map_user.s(0x00b1, 'player stat update 2 notify', + define='SMSG_PLAYER_STAT_UPDATE_2', fixed=[ at(0, u16, 'packet id'), at(2, sp, 'sp type'), at(4, u32, 'value'), ], fixed_size=8, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + One of many player stat packets with no real difference. + ''', ) - map_user.r(0x00b2, 'switch or respawn the character', + map_user.r(0x00b2, 'respawn or switch character', + define='CMSG_PLAYER_REBOOT', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'flag'), ], fixed_size=3, + pre=[HUMAN, 0x01d8], + post=[0x0080, 0x0091, 0x00b0, 0x018b, 0x2b02, 0x2b05], + xpost=[SCRIPT, 0x0081, 0x0088, 0x00a0, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x3011, 0x3022], + desc=''' + If flag is 0, respawn after dying. If flag is 1, try to switch characters. + ''', ) - map_user.s(0x00b3, 'character switch response', + map_user.s(0x00b3, 'character switch success', + define='SMSG_CHAR_SWITCH_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'one'), ], fixed_size=3, - ) - map_user.s(0x00b4, 'npc message', + pre=[0x2b03], + post=[0x0065], + desc=''' + Permission granted to switch characters. + ''', + ) + map_user.s(0x00b4, 'script message notify', + define='SMSG_NPC_MESSAGE', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2122,22 +3072,40 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[MAGIC, SCRIPT, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f], + post=[PRETTY], + desc=''' + An npc is droning on. + ''', ) - map_user.s(0x00b5, 'npc message continues', + map_user.s(0x00b5, '(reverse) script next', + define='SMSG_NPC_NEXT', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, + pre=[SCRIPT], + post=[0x00b9], + desc=''' + An npc paused briefly to catch its breath. + ''', ) - map_user.s(0x00b6, 'npc message ends', + map_user.s(0x00b6, '(reverse) script close', + define='SMSG_NPC_CLOSE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, - ) - map_user.s(0x00b7, 'npc prompts a choice', + pre=[GM, MAGIC, SCRIPT, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f, 0x00b8, 0x00b9, 0x00e6, 0x00f7, 0x0143, 0x0146, 0x01d5], + post=[0x0146], + desc=''' + An npc finally shut up. + ''', + ) + map_user.s(0x00b7, '(reverse) script menu', + define='SMSG_NPC_CHOICE', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2148,31 +3116,58 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[SCRIPT], + post=[0x00b8], + desc=''' + An npc let you choose how it will drone on. + ''', ) - map_user.r(0x00b8, 'send npc response', + map_user.r(0x00b8, '(reverse) script menu result', + define='CMSG_NPC_LIST_CHOICE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'npc id'), at(6, u8, 'menu entry'), ], fixed_size=7, + pre=[0x00b7], + post=[MAGIC, SCRIPT], + xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Choose an entry from the menu (1-based), 0xff to run away. + ''', ) - map_user.r(0x00b9, 'request next npc message', + map_user.r(0x00b9, '(reverse) script next result', + define='CMSG_NPC_NEXT_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'npc id'), ], fixed_size=6, + pre=[0x00b5], + post=[MAGIC, SCRIPT], + xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Let the npc keep breathing. + ''', ) - map_user.r(0x00bb, 'request a stat update', + map_user.r(0x00bb, 'stat increase', + define='CMSG_STAT_UPDATE_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, sp, 'asp'), at(4, u8, 'unused'), ], fixed_size=5, + pre=[HUMAN], + post=[0x00b0, 0x00bc, 0x00be, 0x0141], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b1, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Spend stat points. + ''', ) - map_user.s(0x00bc, 'player stat update 4', + map_user.s(0x00bc, 'stat increase result', + define='SMSG_PLAYER_STAT_UPDATE_4', fixed=[ at(0, u16, 'packet id'), at(2, sp, 'sp type'), @@ -2180,8 +3175,14 @@ def build_context(): at(5, u8, 'val'), ], fixed_size=6, + pre=[SCRIPT, 0x00bb], + post=[PRETTY], + desc=''' + Spent stat points? + ''', ) - map_user.s(0x00bd, 'player stat update 5', + map_user.s(0x00bd, 'player stat update 5 notify', + define='SMSG_PLAYER_STAT_UPDATE_5', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'status point'), @@ -2213,59 +3214,89 @@ def build_context(): at(42, u16, 'manner'), ], fixed_size=44, + pre=[0x007d], + post=[PRETTY], + desc=''' + Bulk notify of stats. + ''', ) - map_user.s(0x00be, 'player stat update 6', + map_user.s(0x00be, 'stat price notify', + define='SMSG_PLAYER_STAT_UPDATE_6', fixed=[ at(0, u16, 'packet id'), at(2, sp, 'sp type'), at(4, u8, 'value'), ], fixed_size=5, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Cost of spending stat points. + ''', ) - map_user.r(0x00bf, 'show an emote', + map_user.r(0x00bf, 'emote', + define='CMSG_PLAYER_EMOTE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'emote'), ], fixed_size=3, + pre=[HUMAN, TIMER], + post=[0x00c0, 0x110], + desc=''' + Don't act like a faceless robot. + ''', ) - map_user.s(0x00c0, 'show the emote of a being', + map_user.s(0x00c0, 'emote notify', + define='SMSG_BEING_EMOTION', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, u8, 'type'), ], fixed_size=7, + pre=[BOOT, FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00bf, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d, 0x3800, 0x3821, 0x3823, 0x3824, 0x3825, 0x3826, 0x3827], + post=[PRETTY], + desc=''' + Somebody is claiming to not be a faceless robot. + ''', ) - map_user.r(0x00c1, 'request online users (unused)', - fixed=[ - at(0, u16, 'packet id'), - ], - fixed_size=2, - ) - map_user.s(0x00c2, 'online users response (unused)', - fixed=[ - at(0, u16, 'packet id'), - at(2, u32, 'users'), - ], - fixed_size=6, - ) - map_user.s(0x00c4, 'npc shop choice', + # 0x00c1 define='CMSG_CHAT_WHO', (unused by this name) + # 0x00c1 define='CMSG_WHO_REQUEST', + # 0x00c2 define='SMSG_WHO_ANSWER', + # 0x00c3 define='SMSG_BEING_CHANGE_LOOKS', + map_user.s(0x00c4, 'npc click result shop', + define='SMSG_NPC_BUY_SELL_CHOICE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, + pre=[MAGIC, SCRIPT, 0x0085, 0x0089, 0x008c, 0x0090, 0x009f], + post=[0x00c5], + desc=''' + That npc you clicked on was a shop. + ''', ) - map_user.r(0x00c5, 'npc shop request', + map_user.r(0x00c5, 'npc shop buy/sell select', + define='CMSG_NPC_BUY_SELL_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, u8, 'type'), ], fixed_size=7, + pre=[0x00c4], + post=[0x00c6, 0x00c7], + desc=''' + Choose whether to buy or sell in a shop. + + type 0: buy + type 1: sell + ''', ) - map_user.s(0x00c6, 'npc buy prompt', + map_user.s(0x00c6, 'npc shop buy select result', + define='SMSG_NPC_BUY', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2278,8 +3309,14 @@ def build_context(): at(9, item_name_id, 'name id'), ], repeat_size=11, - ) - map_user.s(0x00c7, 'npc sell prompt', + pre=[0x00c5], + post=[0x00c8], + desc=''' + List of items you can buy from the shop. + ''', + ) + map_user.s(0x00c7, 'npc shop sell select result', + define='SMSG_NPC_SELL', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2291,8 +3328,17 @@ def build_context(): at(6, u32, 'actual price'), ], repeat_size=10, + pre=[0x00c5], + post=[0x00c9], + desc=''' + List of items you can sell to the shop. + + Currently the server doesn't support customizing this list, + but in theory it could. + ''', ) - map_user.r(0x00c8, 'npc buy request', + map_user.r(0x00c8, 'npc shop buy', + define='CMSG_NPC_BUY_REQUEST', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2303,8 +3349,15 @@ def build_context(): at(2, item_name_id, 'name id'), ], repeat_size=4, - ) - map_user.r(0x00c9, 'npc sell request', + pre=[0x00c6], + post=[0x00a0, 0x00b0, 0x00b1, 0x00ca], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Purchase a bunch of items from a shop. + ''', + ) + map_user.r(0x00c9, 'npc shop sell', + define='CMSG_NPC_SELL_REQUEST', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2315,65 +3368,127 @@ def build_context(): at(2, u16, 'count'), ], repeat_size=4, + pre=[0x00c7], + post=[0x00ac, 0x00af, 0x00b0, 0x00b1, 0x00cb, 0x01d7], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Sell a bunch of items to a shop. + ''', ) - map_user.s(0x00ca, 'npc buy response', + map_user.s(0x00ca, 'npc shop buy result', + define='SMSG_NPC_BUY_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'fail'), ], fixed_size=3, + pre=[0x00c8], + post=[PRETTY], + desc=''' + Result of purchasing items. + ''', ) - map_user.s(0x00cb, 'npc sell response', + map_user.s(0x00cb, 'npc shop sell result', + define='SMSG_NPC_SELL_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'fail'), ], fixed_size=3, + pre=[0x00c9], + post=[PRETTY], + desc=''' + Result of purchasing items. + ''', ) - map_user.s(0x00cd, 'kick status', + # 0x00cc define='CMSG_ADMIN_KICK', + map_user.s(0x00cd, 'kick result', + define='SMSG_ADMIN_KICK_ACK', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[GM], + post=[PRETTY], + desc=''' + Successfully used @kick. + ''', ) - map_user.r(0x00e4, 'trade request request', + # 0x00cf define='CMSG_IGNORE_NICK', + # 0x00d0 define='CMSG_IGNORE_ALL', + # 0x00d2 define='SMSG_IGNORE_ALL_RESPONSE', + map_user.r(0x00e4, 'trade please', + define='CMSG_TRADE_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, + pre=[HUMAN], + post=[0x00e5, 0x00e7, 0x00ee, 0x0110], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Ask someone to trade. + ''', ) map_user.s(0x00e5, 'incoming trade request', + define='SMSG_TRADE_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, char_name, 'char name'), ], fixed_size=26, + pre=[GM, 0x00e4], + post=[0x00e6], + desc=''' + Somebody wants to trade with you. + ''', ) - map_user.r(0x00e6, 'incoming trade request response', + map_user.r(0x00e6, 'incoming trade request result', + define='CMSG_TRADE_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'type'), ], fixed_size=3, + pre=[0x00e5], + post=[0x00e7], + xpost=[MAGIC, SCRIPT, 0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00f8, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + You agree/disagree to begin a trade with someone. + ''', ) - map_user.s(0x00e7, 'trade request response', + map_user.s(0x00e7, 'trade please result', + define='SMSG_TRADE_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'type'), ], fixed_size=3, + pre=[GM, 0x00e4, 0x00e6], + post=[PRETTY], + desc=''' + The original result of asking for a trade. + ''', ) - map_user.r(0x00e8, 'trade item add request', + map_user.r(0x00e8, 'trade add', + define='CMSG_TRADE_ITEM_ADD_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'zeny or ioff2'), at(4, u32, 'amount'), ], fixed_size=8, + pre=[HUMAN], + post=[0x00ac, 0x00e9, 0x01b1, 0x01d7], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Add item/zeny to your trade offer. + ''', ) - map_user.s(0x00e9, 'trade item add', + map_user.s(0x00e9, 'trade item added notify', + define='SMSG_TRADE_ITEM_ADD', fixed=[ at(0, u16, 'packet id'), at(2, u32, 'amount'), @@ -2387,62 +3502,125 @@ def build_context(): at(17, u16, 'card3'), ], fixed_size=19, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Item successfully added. + ''', ) - map_user.r(0x00eb, 'trade add complete', + map_user.r(0x00eb, 'trade lock', + define='CMSG_TRADE_ADD_COMPLETE', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[HUMAN], + post=[0x00ec, 0x1b1], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Indicate readiness to end trade. + + Do this when the other person has added all the items you want. + ''', ) - map_user.s(0x00ec, 'trade ok', + map_user.s(0x00ec, 'trade lock notify', + define='SMSG_TRADE_OK', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'fail'), ], fixed_size=3, + pre=[0x00eb], + post=[PRETTY], + desc=''' + Somebody locked their half of the trade (0=self, 1=other). + ''', ) - map_user.r(0x00ed, 'trace cancel request', + map_user.r(0x00ed, 'trade cancel', + define='CMSG_TRADE_CANCEL_REQUEST', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[HUMAN], + post=[0x00a0, 0x00b0, 0x00ee], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Cancel an ongoing trade. + ''', ) - map_user.s(0x00ee, 'trade cancel', + map_user.s(0x00ee, 'trade cancel notify', + define='SMSG_TRADE_CANCEL', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[], + desc=''' + Somebody cancelled your trade. + ''', ) - map_user.r(0x00ef, 'trade ok request', + map_user.r(0x00ef, 'trade commit', + define='CMSG_TRADE_OK', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[HUMAN], + post=[0x00a0, 0x00af, 0x00b0, 0x00f0], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Actually perform the trade. Requires half-lock from both sides. + ''', ) - map_user.s(0x00f0, 'trade complete', + map_user.s(0x00f0, 'trade complete notify', + define='SMSG_TRADE_COMPLETE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'fail'), ], fixed_size=3, + pre=[0x00ef], + post=[PRETTY], + desc=''' + Trade is finally over. + ''', ) - map_user.s(0x00f2, 'storage status', + map_user.s(0x00f2, 'storage size notify', + define='SMSG_PLAYER_STORAGE_STATUS', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'current slots'), at(4, u16, 'max slots'), ], fixed_size=6, + pre=[GM, SCRIPT, 0x00f3, 0x00f5, 0x3810], + post=[PRETTY], + desc=''' + Update the current/max storage size. + + It's easy to change the max storage on any basis, pity about + the inventory limit ... + ''', ) - map_user.r(0x00f3, 'move item to storage request', + map_user.r(0x00f3, 'storage put', + define='CMSG_MOVE_TO_STORAGE', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), at(4, u32, 'amount'), ], fixed_size=8, + pre=[HUMAN], + post=[0x00ac, 0x00af, 0x00b0, 0x00f2, 0x00f4, 0x01d7], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Move item from inventory to storage. + ''', ) - map_user.s(0x00f4, 'move item to storage', + map_user.s(0x00f4, 'storage added notify', + define='SMSG_PLAYER_STORAGE_ADD', fixed=[ at(0, u16, 'packet id'), at(2, soff1, 'soff1'), @@ -2457,50 +3635,98 @@ def build_context(): at(19, u16, 'card3'), ], fixed_size=21, + pre=[GM, 0x00f3], + post=[PRETTY], + desc=''' + Item was added to your storage. + ''', ) - map_user.r(0x00f5, 'move item from storage request', + map_user.r(0x00f5, 'storage take', + define='CSMG_MOVE_FROM_STORAGE', fixed=[ at(0, u16, 'packet id'), at(2, soff1, 'soff1'), at(4, u32, 'amount'), ], fixed_size=8, + pre=[HUMAN], + post=[0x00a0, 0x00b0, 0x00f2, 0x00f6], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Move item from storage to inventory. + ''', ) - map_user.s(0x00f6, 'remove item from storage', + map_user.s(0x00f6, 'storage removed notify', + define='SMSG_PLAYER_STORAGE_REMOVE', fixed=[ at(0, u16, 'packet id'), at(2, soff1, 'soff1'), at(4, u32, 'amount'), ], fixed_size=8, + pre=[0x00f5], + post=[PRETTY], + desc=''' + Item was removed from your storage. + ''', ) - map_user.r(0x00f7, 'storage close request', + map_user.r(0x00f7, 'storage close', + define='CMSG_CLOSE_STORAGE', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[HUMAN], + post=[MAGIC, SCRIPT, 0x00f8, 0x2b01, 0x3011], + xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b05, 0x3022], + desc=''' + Close your storage. + ''', ) - map_user.s(0x00f8, 'storage closed', + map_user.s(0x00f8, 'storage closed notify', + define='SMSG_PLAYER_STORAGE_CLOSE', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[GM, 0x00e6, 0x00f7], + post=[PRETTY], + desc=''' + Your storage was closed. + ''', ) - map_user.r(0x00f9, 'create party request', + map_user.r(0x00f9, 'party create', + define='CMSG_PARTY_CREATE', fixed=[ at(0, u16, 'packet id'), at(2, party_name, 'party name'), ], fixed_size=26, + pre=[HUMAN], + post=[0x00fa, 0x110, 0x3020], + desc=''' + Create a new party with yourself. + ''', ) - map_user.s(0x00fa, 'create party', + map_user.s(0x00fa, 'party create result', + define='SMSG_PARTY_CREATE', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'flag'), ], fixed_size=3, - ) - map_user.s(0x00fb, 'party info', + pre=[GM, 0x00f9, 0x3820], + post=[PRETTY], + desc=''' + Result of creating a new party. + + flag 0: success + flag 1: bad name + flag 2: already in party + ''', + ) + map_user.s(0x00fb, 'party info notify', + define='SMSG_PARTY_INFO', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2515,69 +3741,130 @@ def build_context(): at(45, u8, 'online'), ], repeat_size=46, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089, 0x3821, 0x3825], + post=[PRETTY], + desc=''' + Full info about your party. + ''', ) - map_user.r(0x00fc, 'party invite request', + map_user.r(0x00fc, 'party invite', + define='CMSG_PARTY_INVITE', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[HUMAN], + post=[0x00fd, 0x00fe], + desc=''' + Invite player to your party. + ''', ) - map_user.s(0x00fd, 'party invite response', + map_user.s(0x00fd, 'party invite result', + define='SMSG_PARTY_INVITE_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, char_name, 'char name'), at(26, u8, 'flag'), ], fixed_size=27, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x00fc, 0x00ff, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d, 0x3822], + post=[PRETTY], + desc=''' + Party invitation response. + + flag 0: already in party + flag 1: reject + flag 2: accept + flag 3: party full + flag 4: same party + ''', ) - map_user.s(0x00fe, 'party invite succeeded', + map_user.s(0x00fe, '(reverse) party invitation', + define='SMSG_PARTY_INVITED', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, party_name, 'party name'), ], fixed_size=30, + pre=[0x00fc], + post=[0x00ff], + desc=''' + You're invited to join someone's party. + ''', ) - map_user.r(0x00ff, 'party join request', + map_user.r(0x00ff, '(reverse) party invitation result', + define='CMSG_PARTY_INVITED', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u32, 'flag'), ], fixed_size=10, + pre=[0x00fe], + post=[0x00fd, 0x0110, 0x3022], + desc=''' + Reply to party invitation. + ''', ) - map_user.r(0x0100, 'party leave request', + map_user.r(0x0100, 'party leave', + define='CMSG_PARTY_LEAVE', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[HUMAN], + post=[0x3024], + desc=''' + I'm sick of your lame party. + ''', ) - map_user.s(0x0101, 'party settings', + map_user.s(0x0101, 'party option notify', + define='SMSG_PARTY_SETTINGS', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'exp'), at(4, u16, 'item'), ], fixed_size=6, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089, 0x3821, 0x3823], + post=[PRETTY], + desc=''' + Party settings changed. + ''', ) - map_user.r(0x0102, 'party settings request', + map_user.r(0x0102, 'party option', + define='CMSG_PARTY_SETTINGS', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'exp'), at(4, u16, 'item'), ], fixed_size=6, + pre=[HUMAN], + post=[0x3023], + desc=''' + Please change party settings. + ''', ) - map_user.r(0x0103, 'party kick request', + map_user.r(0x0103, 'party kick', + define='CMSG_PARTY_KICK', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, char_name, 'unused char name'), ], fixed_size=30, + pre=[HUMAN], + post=[0x3024], + desc=''' + Forcibly remove party member. + ''', ) - map_user.s(0x0105, 'party leave', + # 0x0104 define='SMSG_PARTY_MOVE', + map_user.s(0x0105, 'party left notify', + define='SMSG_PARTY_LEAVE', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -2585,8 +3872,14 @@ def build_context(): at(30, u8, 'flag'), ], fixed_size=31, + pre=[0x3824, 0x3826], + post=[PRETTY], + desc=''' + Somebody got tired of your party. + ''', ) - map_user.s(0x0106, 'update party member hp', + map_user.s(0x0106, 'party hp notify', + define='SMSG_PARTY_UPDATE_HP', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -2594,8 +3887,14 @@ def build_context(): at(8, u16, 'max hp'), ], fixed_size=10, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Party member hp update. + ''', ) - map_user.s(0x0107, 'update party member coords', + map_user.s(0x0107, 'party xy notify', + define='SMSG_PARTY_UPDATE_COORDS', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -2603,8 +3902,14 @@ def build_context(): at(8, u16, 'y'), ], fixed_size=10, - ) - map_user.r(0x0108, 'party message request', + pre=[TIMER], + post=[PRETTY], + desc=''' + Party member location update. + ''', + ) + map_user.r(0x0108, 'party message', + define='CMSG_PARTY_MESSAGE', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2614,8 +3919,14 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, - ) - map_user.s(0x0109, 'party message', + pre=[HUMAN], + post=[0x2b0e, 0x3003, 0x3027], + desc=''' + Talk privately to your party. + ''', + ) + map_user.s(0x0109, 'party message notify', + define='SMSG_PARTY_MESSAGE', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2625,16 +3936,17 @@ def build_context(): repeat=[ at(0, u8, 'c'), ], - repeat_size=1 - ) - map_user.s(0x010c, 'MVP (unused)', - fixed=[ - at(0, u16, 'packet id'), - at(2, block_id, 'block id'), - ], - fixed_size=6, + repeat_size=1, + pre=[0x3827], + post=[PRETTY], + desc=''' + Somebody in your party is talking to you. + ''', ) - map_user.s(0x010e, 'raise a skill', + # 0x010c define='SMSG_MVP', + map_user.s(0x010e, 'skill raise result', + # define='SMSG_GUILD_SKILL_UP', + define='SMSG_PLAYER_SKILL_UP', fixed=[ at(0, u16, 'packet id'), at(2, skill_id, 'skill id'), @@ -2644,8 +3956,14 @@ def build_context(): at(10, u8, 'can raise'), ], fixed_size=11, - ) - map_user.s(0x010f, 'player skills', + pre=[0x0112], + post=[PRETTY], + desc=''' + Successfully raised a skill. + ''', + ) + map_user.s(0x010f, 'skill info notify', + define='SMSG_PLAYER_SKILLS', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2655,8 +3973,14 @@ def build_context(): at(0, skill_info, 'info'), ], repeat_size=37, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Enumeration of your skills. + ''', ) map_user.s(0x0110, 'skill failed', + define='SMSG_SKILL_FAILED', fixed=[ at(0, u16, 'packet id'), at(2, skill_id, 'skill id'), @@ -2666,21 +3990,42 @@ def build_context(): at(9, u8, 'type'), ], fixed_size=10, + pre=[0x0089, 0x00bf, 0x00e4, 0x00f9, 0x00ff], + post=[PRETTY], + desc=''' + Failed to perform a skill. + ''', ) - map_user.r(0x0112, 'request a skill lvl up', + map_user.r(0x0112, 'skill raise', + define='CMSG_SKILL_LEVELUP_REQUEST', fixed=[ at(0, u16, 'packet id'), at(2, skill_id, 'skill id'), ], fixed_size=4, + pre=[HUMAN], + post=[0x00b0, 0x010e, 0x010f], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Spend skill points to raise your skills. + ''', ) - map_user.r(0x0118, 'stop attack request', + # 0x0113 define='CMSG_SKILL_USE_BEING', + # 0x0116 define='CMSG_SKILL_USE_POSITION', + map_user.r(0x0118, 'attack stop', + define='CMSG_PLAYER_STOP_ATTACK', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[HUMAN], + post=[IDLE], + desc=''' + Cancel a continuous attack. + ''', ) - map_user.s(0x0119, 'change player status', + map_user.s(0x0119, 'player option notify', + define='SMSG_PLAYER_STATUS_CHANGE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -2690,8 +4035,16 @@ def build_context(): at(12, u8, 'zero'), ], fixed_size=13, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Update 3/4ths of the option fields. + ''', ) - map_user.s(0x0139, 'move player to within attack range', + # 0x011a define='SMSG_SKILL_NO_DAMAGE', + # 0x011b define='CMSG_SKILL_USE_MAP', + map_user.s(0x0139, 'player move attack range notify', + define='SMSG_PLAYER_MOVE_TO_ATTACK', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -2702,29 +4055,54 @@ def build_context(): at(14, u16, 'range'), ], fixed_size=16, + pre=[0x0089], + post=[PRETTY], + desc=''' + You are moving to enter attack range. + ''', ) - map_user.s(0x013a, 'player attack range', + map_user.s(0x013a, 'player attack range notify', + define='SMSG_PLAYER_ATTACK_RANGE', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'attack range'), ], fixed_size=4, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Update attack range field. + ''', ) - map_user.s(0x013b, 'player arrow message', + map_user.s(0x013b, 'player arrow fail notify', + define='SMSG_PLAYER_ARROW_MESSAGE', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'type'), ], fixed_size=4, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089, 0x00a9], + post=[PRETTY], + desc=''' + Arrow status: failed (0) or equipped (3, ignored). + ''', ) - map_user.s(0x013c, 'player arrow equip', + map_user.s(0x013c, 'player arrow equip notify', + define='SMSG_PLAYER_ARROW_EQUIP', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), ], fixed_size=4, + pre=[0x007d, 0x00a9], + post=[PRETTY], + desc=''' + Arrow equip inventory slot. + ''', ) + # 0x013e define='SMSG_SKILL_CASTING', map_user.s(0x0141, 'player stat update 3', + define='SMSG_PLAYER_STAT_UPDATE_3', fixed=[ at(0, u16, 'packet id'), at(2, sp, 'sp type'), @@ -2733,65 +4111,142 @@ def build_context(): at(10, u32, 'value b e'), ], fixed_size=14, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Update base and cumulative (?) stat points. + ''', ) - map_user.s(0x0142, 'npc integer input', + map_user.s(0x0142, '(reverse) script input integer', + define='SMSG_NPC_INT_INPUT', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, + pre=[SCRIPT], + post=[0x0143], + desc=''' + Npc wants an integer. + ''', ) - map_user.r(0x0143, 'npc integer response', + map_user.r(0x0143, '(reverse) script input integer result', + define='CMSG_NPC_INT_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, u32, 'input int value'), ], fixed_size=10, + pre=[0x0142], + post=[MAGIC, SCRIPT], + xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Npc can have an integer. + ''', ) - map_user.r(0x0146, 'npc close request', + map_user.r(0x0146, '(reverse) script close response', + define='CMSG_NPC_CLOSE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, + pre=[0x00b6], + post=[MAGIC, SCRIPT], + xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Interactive npc chat closed, maybe finish noninteractively now. + ''', ) - map_user.s(0x0147, 'single skill info (unused)', - fixed=[ - at(0, u16, 'packet id'), - at(2, skill_info, 'info'), - ], - fixed_size=39, - ) - map_user.s(0x0148, 'being resurrected', + map_user.s(0x0148, 'being resurrected notify', + define='SMSG_BEING_RESURRECT', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, u16, 'type'), ], fixed_size=8, - ) - map_user.r(0x014d, 'guild check master (unused)', - fixed=[ - at(0, u16, 'packet id'), - ], - fixed_size=2, - ) + pre=[GM], + post=[PRETTY], + desc=''' + It's alive! + ''', + ) + # 0x0149 define='CMSG_ADMIN_MUTE', + # 0x014c define='SMSG_GUILD_ALIANCE_INFO', + # 0x014d define='CMSG_GUILD_CHECK_MASTER', + # 0x014e define='SMSG_GUILD_MASTER_OR_MEMBER', + # 0x014f define='CMSG_GUILD_REQUEST_INFO', + # 0x0151 define='CMSG_GUILD_REQUEST_EMBLEM', + # 0x0152 define='SMSG_GUILD_EMBLEM', + # 0x0153 define='CMSG_GUILD_CHANGE_EMBLEM', + # 0x0154 define='SMSG_GUILD_MEMBER_LIST', + # 0x0155 define='CMSG_GUILD_CHANGE_MEMBER_POS', + # 0x0156 define='SMSG_GUILD_MEMBER_POS_CHANGE', + # 0x0159 define='CMSG_GUILD_LEAVE', + # 0x015a define='SMSG_GUILD_LEAVE', + # 0x015b define='CMSG_GUILD_EXPULSION', + # 0x015c define='SMSG_GUILD_EXPULSION', + # 0x015d define='CMSG_GUILD_BREAK', + # 0x015e define='SMSG_GUILD_BROKEN', + # 0x0160 define='SMSG_GUILD_POS_INFO_LIST', + # 0x0161 define='CMSG_GUILD_CHANGE_POS_INFO', + # 0x0162 define='SMSG_GUILD_SKILL_INFO', + # 0x0163 define='SMSG_GUILD_EXPULSION_LIST', + # 0x0165 define='CMSG_GUILD_CREATE', + # 0x0166 define='SMSG_GUILD_POS_NAME_LIST', + # 0x0167 define='SMSG_GUILD_CREATE_RESPONSE', + # 0x0168 define='CMSG_GUILD_INVITE', + # 0x0169 define='SMSG_GUILD_INVITE_ACK', + # 0x016a define='SMSG_GUILD_INVITE', + # 0x016b define='CMSG_GUILD_INVITE_REPLY', + # 0x016c define='SMSG_GUILD_POSITION_INFO', + # 0x016d define='SMSG_GUILD_MEMBER_LOGIN', + # 0x016e define='CMSG_GUILD_CHANGE_NOTICE', + # 0x016f define='SMSG_GUILD_NOTICE', + # 0x0170 define='CMSG_GUILD_ALLIANCE_REQUEST', + # 0x0171 define='SMSG_GUILD_REQ_ALLIANCE', + # 0x0172 define='CMSG_GUILD_ALLIANCE_REPLY', + # 0x0173 define='SMSG_GUILD_REQ_ALLIANCE_ACK', + # 0x0174 define='SMSG_GUILD_POSITION_CHANGED', + # 0x017e define='CMSG_GUILD_MESSAGE', + # 0x017f define='SMSG_GUILD_MESSAGE', + # 0x0180 define='CMSG_GUILD_OPPOSITION', + # 0x0181 define='SMSG_GUILD_OPPOSITION_ACK', + # 0x0183 define='CMSG_GUILD_ALLIANCE_DELETE', + # 0x0184 define='SMSG_GUILD_DEL_ALLIANCE', map_user.r(0x018a, 'client quit', + define='CMSG_CLIENT_QUIT', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'unused'), ], fixed_size=4, + pre=[NOTHING], + post=[0x018b], + desc=''' + Request explicit end of connection. + ''', ) - map_user.s(0x018b, 'map quit response', + map_user.s(0x018b, 'client quit result', + define='SMSG_MAP_QUIT_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'okay'), ], fixed_size=4, + pre=[GM, 0x00b2, 0x018a], + post=[PRETTY], + desc=''' + Result of explicit end of connection. + ''', ) - map_user.s(0x0195, 'guild party info (unused)', + # 0x0190 define='CMSG_SKILL_USE_POSITION_MORE', + # 0x0193 define='CMSG_SOLVE_CHAR_NAME', + # 0x0194 define='SMSG_SOLVE_CHAR_NAME', + map_user.s(0x0195, 'guild party info notify', + define='SMSG_PLAYER_GUILD_PARTY_INFO', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -2801,8 +4256,14 @@ def build_context(): at(78, str24, 'guild pos again'), ], fixed_size=102, + pre=[0x0094], + post=[PRETTY], + desc=''' + Name of player's party and guild. + ''', ) - map_user.s(0x0196, 'being status change', + map_user.s(0x0196, 'being status change notify', + define='SMSG_BEING_STATUS_CHANGE', fixed=[ at(0, u16, 'packet id'), at(2, status_change, 'sc type'), @@ -2810,16 +4271,32 @@ def build_context(): at(8, u8, 'flag'), ], fixed_size=9, - ) + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Being adds/removes a persistent status effect. + ''', + ) + # 0x0199 define='SMSG_PVP_MAP_MODE', + # 0x019a define='SMSG_PVP_SET', map_user.s(0x019b, 'being effect', + define='SMSG_BEING_SELFEFFECT', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, u32, 'type'), ], fixed_size=10, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089], + post=[PRETTY], + desc=''' + Being runs a one-shot (?) effect. + ''', ) - map_user.s(0x01b1, 'trade item add response', + # 0x019C define='CMSG_ADMIN_LOCAL_ANNOUNCE', + # 0x019D define='CMSG_ADMIN_HIDE', + map_user.s(0x01b1, 'trade add result', + define='SMSG_TRADE_ITEM_ADD_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), @@ -2827,8 +4304,16 @@ def build_context(): at(6, u8, 'fail'), ], fixed_size=7, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Result of trying to add an item/zeny to your trade offer. + ''', ) - map_user.s(0x01c8, 'use inventory item succeeded', + # 0x01b6 define='SMSG_GUILD_BASIC_INFO', + # 0x01b9 define='SMSG_SKILL_CAST_CANCEL', + map_user.s(0x01c8, 'use item result', + define='SMSG_PLAYER_INVENTORY_USE', fixed=[ at(0, u16, 'packet id'), at(2, ioff2, 'ioff2'), @@ -2838,15 +4323,29 @@ def build_context(): at(12, u8, 'ok'), ], fixed_size=13, + pre=[0x00a7], + post=[PRETTY], + desc=''' + You used (or tried to use) an item in your inventory. + + Currently used only in the success case. + ''', ) - map_user.s(0x01d4, 'npc string input', + map_user.s(0x01d4, '(reverse) script input string', + define='SMSG_NPC_STR_INPUT', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), ], fixed_size=6, - ) - map_user.r(0x01d5, 'npc string response', + pre=[SCRIPT], + post=[0x01d5], + desc=''' + Npc wants a string. + ''', + ) + map_user.r(0x01d5, '(reverse) script input string result', + define='CMSG_NPC_STR_RESPONSE', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -2857,8 +4356,15 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x01d4], + post=[MAGIC, SCRIPT], + xpost=[0x0078, 0x007c, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00b6, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Npc can have an integer. + ''', ) - map_user.s(0x01d7, 'change being appearance (unused)', + map_user.s(0x01d7, 'being change look', + define='SMSG_BEING_CHANGE_LOOKS2', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -2867,9 +4373,18 @@ def build_context(): at(9, item_name_id, 'shield'), ], fixed_size=11, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[PRETTY], + desc=''' + Being change appearance. + + This may be a weapon type, an item nameid, or just a value - + it all depends on which look type. + ''', ) # very similar to, but not compatible with, 0x01d9 and 0x01da - map_user.s(0x01d8, 'player update 1', + map_user.s(0x01d8, 'player appear notify', + define='SMSG_PLAYER_UPDATE_1', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -2900,9 +4415,17 @@ def build_context(): at(52, u16, 'unused'), ], fixed_size=54, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[0x0094, 0x00b2], + desc=''' + A player appeared (unmoving). + + Like 0x0078 but for players, and only in the "enter area" case. + ''', ) # very similar to, but not compatible with, 0x01d8 and 0x01da - map_user.s(0x01d9, 'player update 2', + map_user.s(0x01d9, 'player appear notify', + define='SMSG_PLAYER_UPDATE_2', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -2932,9 +4455,17 @@ def build_context(): at(51, u16, 'unused'), ], fixed_size=53, + pre=[0x007d], + post=[0x0094], + desc=''' + A wild player appeared! + + Like 0x0078 but for players, but only in the "spawn on map" case. + ''', ) # very similar to, but not compatible with, 0x01d8 and 0x01d9 - map_user.s(0x01da, 'player move', + map_user.s(0x01da, 'player move notify', + define='SMSG_PLAYER_MOVE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), @@ -2966,8 +4497,17 @@ def build_context(): at(58, u16, 'unused'), ], fixed_size=60, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[0x0094], + desc=''' + A player appeared, moving. + + Like 0x007b but for players. + ''', ) map_user.s(0x01de, 'deal skill damage', + # define='CMSG_LOGIN_REGISTER2', with a different body ... + define='SMSG_SKILL_DAMAGE', fixed=[ at(0, u16, 'packet id'), at(2, skill_id, 'skill id'), @@ -2982,8 +4522,14 @@ def build_context(): at(32, u8, 'type or hit'), ], fixed_size=33, - ) - map_user.s(0x01ee, 'player inventory', + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x0089], + post=[PRETTY], + desc=''' + Took damage from a skill. + ''', + ) + map_user.s(0x01ee, 'inventory list notify', + define='SMSG_PLAYER_INVENTORY', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3002,8 +4548,14 @@ def build_context(): at(16, u16, 'card3'), ], repeat_size=18, - ) - map_user.s(0x01f0, 'storage item list', + pre=[0x007d], + post=[PRETTY], + desc=''' + List of all items in inventory. + ''', + ) + map_user.s(0x01f0, 'storage list notify', + define='SMSG_PLAYER_STORAGE_ITEMS', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3022,16 +4574,32 @@ def build_context(): at(16, u16, 'card3'), ], repeat_size=18, + pre=[GM, SCRIPT, 0x3810], + post=[PRETTY], + desc=''' + List of all items in storage. + ''', ) - map_user.s(0x020c, 'set being ip', + map_user.s(0x020c, 'player ip notify', + define='SMSG_BEING_IP_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'block id'), at(6, ip4, 'ip'), ], fixed_size=10, + pre=[0x0094], + post=[PRETTY], + desc=''' + Show a player's (hashed) IP, for detecting alts. + + The hash is rerandomized every restart. + ''', ) + # 0x0210 define='CMSG_ONLINE_LIST', + # 0x0211 define='SMSG_ONLINE_LIST', map_user.s(0x0212, 'npc command', + define='SMSG_NPC_COMMAND', fixed=[ at(0, u16, 'packet id'), at(2, block_id, 'npc id'), @@ -3041,16 +4609,28 @@ def build_context(): at(14, u16, 'y'), ], fixed_size=16, - ) - + pre=[NOTHING], + post=[PRETTY], + desc=''' + Make an npc do fancy things (for Manaplus). + ''', + ) + # 0x0213 define='CMSG_SET_STATUS', + # 0x0214 define='SMSG_QUEST_SET_VAR', + # 0x0215 define='SMSG_QUEST_PLAYER_VARS', + # 0x0220 define='SMSG_BEING_NAME_RESPONSE2', + # 0x0221 define='SMSG_CHAR_CREATE_SUCCEEDED2', + # 0x0222 define='CMSG_CHAT_MESSAGE2', + # 0x0223 define='SMSG_BEING_CHAT2', + # 0x0224 define='SMSG_PLAYER_CHAT2', + # 0x0225 define='SMSG_BEING_MOVE3', + # 0x0226 define='SMSG_MAP_MASK', + # 0x0227 define='SMSG_MAP_MUSIC', + # 0x0228 define='SMSG_NPC_CHANGETITLE', + + # TOC_LOGINCHAR # login char - login_char.r(0x2709, 'reload gm accounts request', - fixed=[ - at(0, u16, 'packet id'), - ], - fixed_size=2, - ) - login_char.r(0x2710, 'add char server request', + login_char.r(0x2710, 'server connect char', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), @@ -3064,15 +4644,25 @@ def build_context(): at(84, u16, 'is new'), ], fixed_size=86, + pre=[BOOT], + post=[0x2711, 0x2732], + desc=''' + Become an authenticated char server. + ''', ) - login_char.s(0x2711, 'add char server result', + login_char.s(0x2711, 'server connect char result', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, + pre=[0x2710], + post=[FINISH, IDLE], + desc=''' + Result of auth'ing as a char server. + ''', ) - login_char.r(0x2712, 'account auth request', + login_char.r(0x2712, 'account auth', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3082,6 +4672,11 @@ def build_context(): at(15, ip4, 'ip'), ], fixed_size=19, + pre=[0x0065], + post=[0x2713, 0x2729], + desc=''' + Check whether client's cookies are okay. + ''', ) login_char.s(0x2713, 'account auth result', fixed=[ @@ -3089,35 +4684,60 @@ def build_context(): at(2, account_id, 'account id'), at(6, u8, 'invalid'), at(7, account_email, 'email'), - at(47, time32, 'connect until'), + at(47, time32, 'unused connect until'), ], fixed_size=51, + pre=[0x2712], + post=[0x006b, 0x006c], + desc=''' + Send account auth status to tmwa-char. + + Status: + 0: good + 1: bad + ''', ) - login_char.r(0x2714, 'online count', + login_char.r(0x2714, 'online count notify', fixed=[ at(0, u16, 'packet id'), at(2, u32, 'users'), ], fixed_size=6, + pre=[TIMER], + post=[IDLE], + desc=''' + Update count of online users. + + This occurs every few seconds, so is also used for antifreeze if enabled. + ''', ) - login_char.r(0x2716, 'email limit request', + login_char.r(0x2716, 'email limit', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[0x0065], + post=[0x2717], + desc=''' + Fetch current email (and previously, validity limit) for an account. + ''', ) login_char.s(0x2717, 'email limit result', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_email, 'email'), - at(46, time32, 'connect until'), + at(46, time32, 'unused connect until'), ], fixed_size=50, + pre=[0x2716], + post=[IDLE], + desc=''' + Yield current email (and previously, validity limit) for an account. + ''', ) - # 0x2b0a - login_char.r(0x2720, 'become gm request', + login_char.r(0x2720, 'become gm account', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3126,17 +4746,26 @@ def build_context(): head_size=8, repeat=[at(0, u8, 'c')], repeat_size=1, + pre=[0x2b0a], + post=[0x2721, 0x2732], + desc=''' + Grant privileges by password. + ''', ) - login_char.s(0x2721, 'become gm reply', + login_char.s(0x2721, 'become gm account reply', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, gm, 'gm level'), ], fixed_size=10, + pre=[0x2720], + post=[0x2b0b], + desc=''' + Maybe granted privileges? + ''', ) - # 0x2b0c - login_char.r(0x2722, 'account email change request', + login_char.r(0x2722, 'account email change', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3144,14 +4773,24 @@ def build_context(): at(46, account_email, 'new email'), ], fixed_size=86, + pre=[0x2b0c], + post=[IDLE], + desc=''' + Change account email. + ''', ) - login_char.s(0x2723, 'changesex reply', + login_char.s(0x2723, 'changesex result/notify', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, sex, 'sex'), ], fixed_size=7, + pre=[0x2727, 0x793c], + post=[0x2b0d], + desc=''' + Set was flipped (if from char server) or set to an absolute value (if set from admin). + ''', ) login_char.r(0x2724, 'block status', fixed=[ @@ -3160,6 +4799,15 @@ def build_context(): at(6, u32, 'status'), ], fixed_size=10, + pre=[0x2b0e], + post=[0x2731], + desc=''' + Set an account's blocked status, see 0x006a but add one. + + The only really useful values here are 0 ("unblock") and + 5 ("block"). Don't use this for state 7 ("ban") because it + requires a timestamp argument. + ''', ) login_char.r(0x2725, 'ban add', fixed=[ @@ -3168,6 +4816,12 @@ def build_context(): at(6, human_time_diff, 'ban add'), ], fixed_size=18, + pre=[0x2b0e], + post=[0x2731], + desc=''' + Adjust the time of an account's temporary ban. If the account + is not already banned, first set the ban to the current time. + ''', ) # evil packet, see also 0x794e login_admin.s(0x2726, 'broadcast', @@ -3181,15 +4835,24 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x794e], + post=[0x3800], + desc=''' + This packet is evil. + ''', ) - login_char.r(0x2727, 'change sex request', + login_char.r(0x2727, 'change sex', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[0x2b0e], + post=[0x2723], + desc=''' + Flip account's gender. + ''', ) - # 0x2b10, 0x2b11 login_char.r(0x2728, 'update account reg2', head=[ at(0, u16, 'packet id'), @@ -3202,8 +4865,19 @@ def build_context(): at(32, u32, 'value'), ], repeat_size=36, + pre=[0x2b10], + post=[0x2729], + desc=''' + Update account's login-stored variables. + + These start with ## and are unused because: + + 1. They didn't work at all before I started working on the server. + 2. We only run one char-server so we might as well store vars there. + 3. The meaning would be weird anyway. + ''', ) - login_char.s(0x2729, 'update account reg2', + login_char.s(0x2729, 'update account reg2 notify', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3215,22 +4889,40 @@ def build_context(): at(32, u32, 'value'), ], repeat_size=36, + pre=[0x2712, 0x2728], + post=[0x2b11], + desc=''' + Updated account's ##variables. + + This is sent both in response to 0x2728 and in response to + 0x2712 cookie check. + ''' ) - login_char.r(0x272a, 'unban request', + login_char.r(0x272a, 'unban', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[0x2b0e], + post=[IDLE], + desc=''' + Clear an account's temporary ban date. + ''', ) - login_char.s(0x2730, 'account deleted', + login_char.s(0x2730, 'account deleted notify', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[0x7932], + post=[0x2afe, 0x2b12, 0x2b13, 0x3821, 0x3824, 0x3826], + desc=''' + Account just doesn't exist anymore. + ''', ) - login_char.s(0x2731, 'status or ban changed', + login_char.s(0x2731, 'status or ban changed notify', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3238,8 +4930,13 @@ def build_context(): at(7, time32, 'status or ban until'), ], fixed_size=11, + pre=[0x2724, 0x2725, 0x7936, 0x794a, 0x794c], + post=[0x2b14], + desc=''' + Account has a new ban date or block status. + ''', ) - login_char.s(0x2732, 'gm account list', + login_char.s(0x2732, 'gm account list notify', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3250,8 +4947,13 @@ def build_context(): at(4, gm1, 'gm level'), ], repeat_size=5, + pre=[TIMER, 0x2710, 0x2720, 0x793e, 0x7955], + post=[0x2b15], + desc=''' + Send GM accounts to all character servers. + ''', ) - login_char.r(0x2740, 'change password request', + login_char.r(0x2740, 'change password', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3259,24 +4961,29 @@ def build_context(): at(30, account_pass, 'new pass'), ], fixed_size=54, + pre=[0x0061], + post=[0x2741], + desc=''' + Change password of an account. + ''', ) - login_char.s(0x2741, 'change password reply', + login_char.s(0x2741, 'change password result', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'status'), ], fixed_size=7, + pre=[0x2740], + post=[0x0062], + desc=''' + Password changed or not. + ''', ) + # TOC_CHARMAP # char map - char_map.r(0x2af7, 'reload gm db', - fixed=[ - at(0, u16, 'packet id'), - ], - fixed_size=2, - ) - char_map.r(0x2af8, 'add map server request', + char_map.r(0x2af8, 'server connect map', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), @@ -3286,15 +4993,27 @@ def build_context(): at(58, u16, 'port'), ], fixed_size=60, + pre=[TIMER], + post=[0x2af9, 0x2b15], + desc=''' + Become an authenticated map server. + ''', ) - char_map.s(0x2af9, 'add map server result', + char_map.s(0x2af9, 'server connect map result', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'code'), ], fixed_size=3, + pre=[0x2af8], + post=[FINISH, SCRIPT, 0x2afa], + desc=''' + Did I auth? + ''', ) # wtf duplicate v + # formerly 0x2afa, now fixed, but now sorted wrong + # (should go below, but don't want to break diff) char_map.r(0x2afa, 'map list', head=[ at(0, u16, 'packet id'), @@ -3305,25 +5024,34 @@ def build_context(): at(0, map_name, 'map name'), ], repeat_size=16, + pre=[0x2af9], + post=[0x2afb, 0x2b04], + desc=''' + Tell the char server what maps I have. + ''', ) # wtf duplicate ^ - char_map.s(0x2afa, 'itemfrob', - fixed=[ - at(0, u16, 'packet id'), - at(2, item_name_id4, 'source item id'), - at(6, item_name_id4, 'dest item id'), - ], - fixed_size=10, - ) - char_map.s(0x2afb, 'map list ack', + # formerly 0x2afa, now fixed, but now sorted wrong + # (should go below, but don't want to break diff) + # + # (now erased) + + char_map.s(0x2afb, 'map list result', fixed=[ at(0, u16, 'packet id'), at(2, u8, 'unknown'), at(3, char_name, 'whisper name'), ], fixed_size=27, + pre=[0x2afa], + post=[FINISH, IDLE], + desc=''' + I got your maps. + + (having pronoun consistency problems here) + ''', ) - char_map.r(0x2afc, 'character auth request', + char_map.r(0x2afc, 'character auth', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3333,19 +5061,30 @@ def build_context(): at(18, ip4, 'ip'), ], fixed_size=22, + pre=[0x0072], + post=[0x2afd, 0x2afe], + desc=''' + Check whether client's cookies are okay. + ''', ) - char_map.s(0x2afd, 'character auth and data', + char_map.s(0x2afd, 'character auth success', payload=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), at(4, account_id, 'account id'), at(8, u32, 'login id2'), - at(12, time32, 'connect until'), + at(12, time32, 'unused connect until'), at(16, u16, 'packet tmw version'), at(18, char_key, 'char key'), at(None, char_data, 'char data'), ], payload_size=None, + pre=[0x2afc], + post=[0x0073, 0x0081, 0x0091, 0x3005, 0x3021], + xpost=[GM, SCRIPT, 0x0080, 0x0088, 0x00a0, 0x00ac, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d7, 0x01d8, 0x01da, 0x2b01, 0x2b05, 0x3011, 0x3022], + desc=''' + Account authentication succeeded. + ''', ) char_map.s(0x2afe, 'character auth error', fixed=[ @@ -3353,6 +5092,11 @@ def build_context(): at(2, account_id, 'account id'), ], fixed_size=6, + pre=[0x0068, 0x2730, 0x2afc], + post=[0x0081], + desc=''' + Send account id to tmwa-map for disconnection. + ''', ) char_map.r(0x2aff, 'user list', head=[ @@ -3365,6 +5109,11 @@ def build_context(): at(0, char_id, 'char id'), ], repeat_size=4, + pre=[TIMER], + post=[IDLE], + desc=''' + Where can everybody be found? + ''', ) char_map.s(0x2b00, 'total users', fixed=[ @@ -3372,6 +5121,11 @@ def build_context(): at(2, u32, 'users'), ], fixed_size=6, + pre=[TIMER], + post=[IDLE], + desc=''' + How many people are there in total? + ''', ) char_map.r(0x2b01, 'character save', payload=[ @@ -3383,8 +5137,13 @@ def build_context(): at(None, char_data, 'char data'), ], payload_size=None, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[IDLE], + desc=''' + Full character save. + ''', ) - char_map.r(0x2b02, 'char select req', + char_map.r(0x2b02, 'char select', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3393,16 +5152,26 @@ def build_context(): at(14, ip4, 'ip'), ], fixed_size=18, + pre=[0x00b2], + post=[0x2b03], + desc=''' + Client is going back to select character again. + ''', ) - char_map.s(0x2b03, 'char select res', + char_map.s(0x2b03, 'char select result', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'unknown'), ], fixed_size=7, + pre=[0x2b02], + post=[0x00b3], + desc=''' + Client can go back to select character again. + ''', ) - char_map.s(0x2b04, 'map list broadcast', + char_map.s(0x2b04, 'map list notify', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3414,8 +5183,13 @@ def build_context(): at(0, map_name, 'map name'), ], repeat_size=16, + pre=[0x2afa], + post=[IDLE], + desc=''' + Send remote map information to everyone. + ''', ) - char_map.r(0x2b05, 'change map server request', + char_map.r(0x2b05, 'change map server', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3431,6 +5205,11 @@ def build_context(): at(45, ip4, 'client ip'), ], fixed_size=49, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[0x2b06], + desc=''' + Client wants to use a remote map. + ''', ) char_map.s(0x2b06, 'change map server ack', fixed=[ @@ -3446,9 +5225,13 @@ def build_context(): at(42, u16, 'map port'), ], fixed_size=44, + pre=[0x2b05], + post=[0x0081, 0x0092], + desc=''' + Client can use a remote map. + ''', ) - # 0x2720 - char_map.r(0x2b0a, 'become gm request', + char_map.r(0x2b0a, 'become gm', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3457,6 +5240,11 @@ def build_context(): head_size=8, repeat=[at(0, u8, 'c')], repeat_size=1, + pre=[GM], + post=[0x2720, 0x2b0b], + desc=''' + Get a GM level from a password. + ''', ) char_map.s(0x2b0b, 'become gm result', fixed=[ @@ -3465,9 +5253,13 @@ def build_context(): at(6, gm, 'gm level'), ], fixed_size=10, + pre=[0x2721, 0x2b0a], + post=[IDLE], + desc=''' + Got a GM level from a password, or not. + ''', ) - # 0x2722 - char_map.r(0x2b0c, 'change email request', + char_map.r(0x2b0c, 'change email', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3475,6 +5267,11 @@ def build_context(): at(46, account_email, 'new email'), ], fixed_size=86, + pre=[GM], + post=[0x2722], + desc=''' + Player used @email. + ''', ) char_map.s(0x2b0d, 'sex changed notify', fixed=[ @@ -3483,8 +5280,14 @@ def build_context(): at(6, sex, 'sex'), ], fixed_size=7, + pre=[0x2723], + post=[0x00ac, 0x01d7, 0x2b01, 0x3011], + xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b05, 0x3022], + desc=''' + Kick someone who had a sex-change operation. + ''', ) - char_map.r(0x2b0e, 'named char operation request', + char_map.r(0x2b0e, 'named char operation', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3493,8 +5296,13 @@ def build_context(): at(32, human_time_diff, 'ban add'), ], fixed_size=44, + pre=[GM, SCRIPT, 0x008c, 0x0096, 0x0108], + post=[0x2724, 0x2725, 0x2727, 0x272a, 0x2b0f], + desc=''' + Perform block/ban/unblock/unban/sexchange by name. + ''', ) - char_map.r(0x2b0f, 'named char operation answer', + char_map.r(0x2b0f, 'named char operation result', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3503,9 +5311,13 @@ def build_context(): at(32, u16, 'error'), ], fixed_size=34, + pre=[0x2b0e], + post=[IDLE], + desc=''' + Maybe performed block/ban/unblock/unban/sexchange by name. + ''', ) - # 0x2728, 0x2729 - char_map.r(0x2b10, 'account reg2 update', + char_map.r(0x2b10, 'account reg2', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3517,8 +5329,13 @@ def build_context(): at(32, u32, 'value'), ], repeat_size=36, + pre=[SCRIPT], + post=[0x2728], + desc=''' + Update login-stored ##registers. + ''', ) - char_map.s(0x2b11, 'account reg2 update', + char_map.s(0x2b11, 'account reg2 notify', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3530,6 +5347,11 @@ def build_context(): at(32, u32, 'value'), ], repeat_size=36, + pre=[0x2729], + post=[IDLE], + desc=''' + Broadcast login-stored ##registers. + ''', ) char_map.s(0x2b12, 'divorce notify', fixed=[ @@ -3538,6 +5360,11 @@ def build_context(): at(6, char_id, 'partner id'), ], fixed_size=10, + pre=[0x0068, 0x2730, 0x2b16], + post=[IDLE], + desc=''' + Somebody either performed a divorce or had it done to them. + ''', ) char_map.s(0x2b13, 'account delete notify', fixed=[ @@ -3545,6 +5372,11 @@ def build_context(): at(2, account_id, 'account id'), ], fixed_size=6, + pre=[0x2730], + post=[IDLE], + desc=''' + Disconnect player who was deleted. + ''', ) char_map.s(0x2b14, 'status or ban notify', fixed=[ @@ -3554,6 +5386,11 @@ def build_context(): at(7, time32, 'status or ban until'), ], fixed_size=11, + pre=[0x2731], + post=[IDLE], + desc=''' + Somebody was banned or otherwise had their status changed. + ''', ) char_map.s(0x2b15, 'gm account list notify', head=[ @@ -3566,16 +5403,29 @@ def build_context(): at(4, gm1, 'gm level'), ], repeat_size=5, + pre=[0x2732, 0x2af8], + post=[IDLE], + desc=''' + Update full list of GM accounts. + ''', ) - char_map.r(0x2b16, 'divorce request', + char_map.r(0x2b16, 'divorce', fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), ], fixed_size=6, + pre=[SCRIPT], + post=[0x2b12], + desc=''' + Live the single life again. + ''', ) + # 2bfa/2bfb are injected above + + # TOC_INTERMAP - char_map.r(0x3000, 'gm broadcast', + char_map.r(0x3000, 'gm broadcast begin', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3585,8 +5435,13 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[GM, SCRIPT], + post=[0x3800], + desc=''' + Yell at everybody. + ''', ) - char_map.r(0x3001, 'whisper forward', + char_map.r(0x3001, 'whisper remote begin', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3598,17 +5453,26 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x0096], + post=[0x3801, 0x3802], + desc=''' + Begin forwarding a private message to a nonlocal map server. + ''', ) - char_map.r(0x3002, 'whisper forward result', + char_map.r(0x3002, 'whisper remote forward result', fixed=[ at(0, u16, 'packet id'), at(2, char_id, 'char id'), at(6, u8, 'flag'), ], fixed_size=7, + pre=[0x3801], + post=[0x3802], + desc=''' + Intermediate result of forwarding a private message to a nonlocal map server. + ''', ) - # 0x3803 - char_map.r(0x3003, 'wgm forward', + char_map.r(0x3003, 'wgm remote begin', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3620,8 +5484,12 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[GM, 0x008c, 0x0096, 0x0108], + post=[0x3803], + desc=''' + Begin GM whisper to all map servers. + ''', ) - # 0x3804 char_map.r(0x3004, 'save account reg', head=[ at(0, u16, 'packet id'), @@ -3634,6 +5502,11 @@ def build_context(): at(32, u32, 'value'), ], repeat_size=36, + pre=[MAGIC, SCRIPT], + post=[0x3804], + desc=''' + Save all char-server-stored account registers. + ''', ) char_map.r(0x3005, 'want account reg', fixed=[ @@ -3641,15 +5514,25 @@ def build_context(): at(2, account_id, 'account id'), ], fixed_size=6, + pre=[0x2afd], + post=[0x3804], + desc=''' + Explicitly request values of char-server-stored account registers. + ''', ) - char_map.r(0x3010, 'want storage', + char_map.r(0x3010, 'load storage', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[GM, SCRIPT], + post=[0x3810], + desc=''' + Request access to storage. + ''', ) - char_map.r(0x3011, 'got storage', + char_map.r(0x3011, 'save storage', payload=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3657,8 +5540,13 @@ def build_context(): at(8, storage, 'storage'), ], payload_size=None, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[0x3811], + desc=''' + Transmission of changed storage. + ''', ) - char_map.r(0x3020, 'create party', + char_map.r(0x3020, 'party create', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3668,13 +5556,23 @@ def build_context(): at(70, u16, 'level'), ], fixed_size=72, + pre=[GM, 0x00f9], + post=[0x3820, 0x3821], + desc=''' + Request creation of a party. + ''', ) - char_map.r(0x3021, 'request party info', + char_map.r(0x3021, 'party needinfo', fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), ], fixed_size=6, + pre=[0x2afd], + post=[0x3821], + desc=''' + Request full info about a party. + ''', ) char_map.r(0x3022, 'party add member', fixed=[ @@ -3686,6 +5584,11 @@ def build_context(): at(50, u16, 'level'), ], fixed_size=52, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0085, 0x0089, 0x008c, 0x009f, 0x00a2, 0x00a7, 0x00a9, 0x00ab, 0x00b2, 0x00b8, 0x00b9, 0x00bb, 0x00c8, 0x00c9, 0x00e4, 0x00e6, 0x00e8, 0x00eb, 0x00ed, 0x00ef, 0x00f3, 0x00f5, 0x00f7, 0x00ff, 0x0112, 0x0143, 0x0146, 0x01d5, 0x2afd, 0x2b0d], + post=[0x3821, 0x3822, 0x3823], + desc=''' + Request to add a party member in a slot somewhere. + ''', ) char_map.r(0x3023, 'party change option', fixed=[ @@ -3696,6 +5599,11 @@ def build_context(): at(12, u16, 'item'), ], fixed_size=14, + pre=[0x0102], + post=[0x3823], + desc=''' + Explicitly request a change of party settings. + ''', ) char_map.r(0x3024, 'party leave', fixed=[ @@ -3704,6 +5612,11 @@ def build_context(): at(6, account_id, 'account id'), ], fixed_size=10, + pre=[0x0100, 0x0103, 0x3822], + post=[0x3821, 0x3824, 0x3826], + desc=''' + Remove a member from party. + ''', ) char_map.r(0x3025, 'party change map', fixed=[ @@ -3715,8 +5628,17 @@ def build_context(): at(27, u16, 'level'), ], fixed_size=29, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089], + post=[0x3823, 0x3825], + desc=''' + A party member has: + + 1. loaded a new map + 2. levelled up + 3. logged out + ''', ) - char_map.r(0x3027, 'party message', + char_map.r(0x3027, 'party message remote begin', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3728,6 +5650,11 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x0108], + post=[0x3827], + desc=''' + Begin to send a party message to other map servers. + ''', ) char_map.r(0x3028, 'party check conflict', fixed=[ @@ -3737,9 +5664,16 @@ def build_context(): at(10, char_name, 'char name'), ], fixed_size=34, + pre=[FINISH, GM, MAGIC, SCRIPT, TIMER, 0x007d, 0x0089, 0x3822], + post=[0x3821, 0x3824, 0x3826], + desc=''' + Explicitly request a conflict check. + + (I am not sure this is actually meaningful). + ''', ) - char_map.s(0x3800, 'gm broadcast', + char_map.s(0x3800, 'gm broadcast forward', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3749,8 +5683,13 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x2726, 0x3000], + post=[0x009a, 0x00c0], + desc=''' + Yell, reaching everybody. + ''', ) - char_map.s(0x3801, 'whisper forward', + char_map.s(0x3801, 'whisper remote forward', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3763,17 +5702,26 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x3001], + post=[0x0097, 0x3002], + desc=''' + Intermediate forwarding a private message to a nonlocal map server. + ''', ) - char_map.s(0x3802, 'whisper result', + char_map.s(0x3802, 'whisper remote begin result', fixed=[ at(0, u16, 'packet id'), at(2, char_name, 'sender char name'), at(26, u8, 'flag'), ], fixed_size=27, + pre=[0x3001, 0x3002], + post=[0x0098], + desc=''' + Final result of forwarding a private message to a nonlocal map server. + ''', ) - # 0x3003 - char_map.s(0x3803, 'whisper gm', + char_map.s(0x3803, 'wgm remote forward', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3785,9 +5733,13 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x3003], + post=[0x0097], + desc=''' + Forward of GM whisper. + ''', ) - # 0x3004 - char_map.s(0x3804, 'broadcast account reg', + char_map.s(0x3804, 'account reg notify', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3799,8 +5751,15 @@ def build_context(): at(32, u32, 'value'), ], repeat_size=36, + pre=[0x3004, 0x3005], + post=[IDLE], + desc=''' + Char-server-stored account regs have changed. + + Also sent on explicit request. + ''', ) - char_map.s(0x3810, 'load storage', + char_map.s(0x3810, 'load storage result', payload=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3808,16 +5767,26 @@ def build_context(): at(8, storage, 'storage'), ], payload_size=None, + pre=[0x3010], + post=[0x00a6, 0x00f2, 0x01f0], + desc=''' + Transmission of requested storage. + ''', ) - char_map.s(0x3811, 'save storage ack', + char_map.s(0x3811, 'save storage result', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, u8, 'unknown'), ], fixed_size=7, + pre=[0x3011], + post=[IDLE], + desc=''' + Acknowledgement of saved storage. + ''', ) - char_map.s(0x3820, 'party created', + char_map.s(0x3820, 'party create result', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), @@ -3826,8 +5795,13 @@ def build_context(): at(11, party_name, 'party name'), ], fixed_size=35, + pre=[0x3020], + post=[0x00fa], + desc=''' + Result of requesting party creation. + ''', ) - char_map.s(0x3821, 'party info maybe', + char_map.s(0x3821, 'party needinfo result', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3838,8 +5812,16 @@ def build_context(): at(0, party_most, 'party most'), ], option_size=None, + pre=[0x0068, 0x2730, 0x3020, 0x3021, 0x3022, 0x3024, 0x3028], + post=[0x00c0, 0x00fb, 0x0101], + desc=''' + Full info about a party, it present. + + Payload may be missing if no such party (*should* not + happen, but ...). + ''', ) - char_map.s(0x3822, 'party member added', + char_map.s(0x3822, 'party add member result', fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), @@ -3847,8 +5829,15 @@ def build_context(): at(10, u8, 'flag'), ], fixed_size=11, + pre=[0x3022], + post=[0x00fd, 0x3024, 0x3028], + desc=''' + Result of trying to add a party member. + + Flag is 1 if party is no slots or something. + ''', ) - char_map.s(0x3823, 'party option changed', + char_map.s(0x3823, 'party change option notify', fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), @@ -3858,8 +5847,15 @@ def build_context(): at(14, u8, 'flag'), ], fixed_size=15, + pre=[0x3022, 0x3023, 0x3025], + post=[0x00c0, 0x0101], + desc=''' + Party options were changed. + + Often generated implicitly. + ''', ) - char_map.s(0x3824, 'party member left', + char_map.s(0x3824, 'party leave notify', fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), @@ -3867,8 +5863,13 @@ def build_context(): at(10, char_name, 'char name'), ], fixed_size=34, + pre=[0x0068, 0x2730, 0x3024, 0x3028], + post=[0x00c0, 0x0105], + desc=''' + Member is no longer in party. + ''', ) - char_map.s(0x3825, 'party member moved', + char_map.s(0x3825, 'party change map notify', fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), @@ -3878,16 +5879,26 @@ def build_context(): at(27, u16, 'level'), ], fixed_size=29, + pre=[0x3025], + post=[0x00c0, 0x00fb], + desc=''' + Party member location/level/online status has changed. + ''', ) - char_map.s(0x3826, 'party broken', + char_map.s(0x3826, 'party broken notify', fixed=[ at(0, u16, 'packet id'), at(2, party_id, 'party id'), at(6, u8, 'flag'), ], fixed_size=7, + pre=[BOOT, 0x0068, 0x2730, 0x3024, 0x3028], + post=[0x00c0, 0x0105], + desc=''' + A party just isn't there anymore. + ''', ) - char_map.s(0x3827, 'party message', + char_map.s(0x3827, 'party message remote forward', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3899,37 +5910,67 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x3027], + post=[0x00c0, 0x0109], + desc=''' + Actually send a party message to other map servers. + ''', ) + # TOC_MISC # any client - any_user.r(0x7530, 'version request', + any_user.r(0x7530, 'version', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[ADMIN, BOOT, 0x7919], + post=[0x7531], + desc=''' + Request from client or ladmin for server version. + ''', ) - any_user.s(0x7531, 'version reply', + any_user.s(0x7531, 'version result', fixed=[ at(0, u16, 'packet id'), at(2, version, 'version'), ], fixed_size=10, + pre=[0x7530], + post=[0x0064], + desc=''' + Response to client's request for server version. + ''', ) - any_user.r(0x7532, 'shutdown please', + any_user.r(0x7532, 'disconnect', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[HUMAN], + post=[FINISH], + desc=''' + Request from client or ladmin to disconnect. + ''', ) + # 0x7530 define='CMSG_SERVER_VERSION_REQUEST', + # 0x7531 define='SMSG_SERVER_VERSION_RESPONSE', + # 0x7532 define='CMSG_CLIENT_DISCONNECT', + # TOC_LOGINADMIN # login admin - login_admin.r(0x7918, 'admin auth request', + login_admin.r(0x7918, 'admin auth', fixed=[ at(0, u16, 'packet id'), at(2, u16, 'encryption zero'), at(4, account_pass, 'account pass'), ], fixed_size=28, + pre=[BOOT], + post=[0x7919], + desc=''' + Authenticate as an admin. + ''', ) login_admin.s(0x7919, 'admin auth result', fixed=[ @@ -3937,16 +5978,26 @@ def build_context(): at(2, u8, 'error'), ], fixed_size=3, + pre=[0x7918], + post=[0x7530], + desc=''' + Status of admin authentication. + ''', ) - login_admin.r(0x7920, 'account list request', + login_admin.r(0x7920, 'account list', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'start account id'), at(6, account_id, 'end account id'), ], fixed_size=10, + pre=[ADMIN, 0x7921], + post=[0x7921], + desc=''' + Request list of all accounts (between bounds if given). + ''', ) - login_admin.s(0x7921, 'account list reply', + login_admin.s(0x7921, 'account list result', head=[ at(0, u16, 'packet id'), at(2, u16, 'packet length'), @@ -3961,22 +6012,13 @@ def build_context(): at(34, u32, 'status'), ], repeat_size=38, + pre=[0x7920], + post=[IDLE, 0x7920], + desc=''' + May be truncated, if so should retry with new lower bound. + ''', ) - login_admin.r(0x7924, 'itemfrob', - fixed=[ - at(0, u16, 'packet id'), - at(2, item_name_id4, 'source item id'), - at(6, item_name_id4, 'dest item id'), - ], - fixed_size=10, - ) - login_admin.s(0x7925, 'itemfrob ok', - fixed=[ - at(0, u16, 'packet id'), - ], - fixed_size=2, - ) - login_admin.r(0x7930, 'account create request', + login_admin.r(0x7930, 'account create', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), @@ -3985,6 +6027,11 @@ def build_context(): at(51, account_email, 'email'), ], fixed_size=91, + pre=[ADMIN], + post=[0x7931], + desc=''' + Account creation request. + ''', ) login_admin.s(0x7931, 'account create result', fixed=[ @@ -3993,29 +6040,49 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x7930], + post=[IDLE], + desc=''' + Account creation response. + ''', ) - login_admin.r(0x7932, 'account delete request', + login_admin.r(0x7932, 'account delete', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), ], fixed_size=26, + pre=[ADMIN], + post=[0x2730, 0x7933], + desc=''' + Account deletion request. + ''', ) - login_admin.s(0x7933, 'account delete reply', + login_admin.s(0x7933, 'account delete result', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x7932], + post=[IDLE], + desc=''' + Account deletion response. + ''', ) - login_admin.r(0x7934, 'password change request', + login_admin.r(0x7934, 'password change', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_pass, 'password'), ], fixed_size=50, + pre=[ADMIN], + post=[0x7935], + desc=''' + Change password request. + ''', ) login_admin.s(0x7935, 'password change result', fixed=[ @@ -4024,8 +6091,13 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x7934], + post=[IDLE], + desc=''' + Change password response. + ''', ) - login_admin.r(0x7936, 'account state change request', + login_admin.r(0x7936, 'account state change', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), @@ -4033,6 +6105,11 @@ def build_context(): at(30, seconds, 'error message'), ], fixed_size=50, + pre=[ADMIN], + post=[0x2731, 0x7937], + desc=''' + Account state change request. + ''', ) login_admin.s(0x7937, 'account state change result', fixed=[ @@ -4042,12 +6119,22 @@ def build_context(): at(30, u32, 'status'), ], fixed_size=34, + pre=[0x7936], + post=[IDLE], + desc=''' + Account state change response. + ''', ) - login_admin.r(0x7938, 'server list request', + login_admin.r(0x7938, 'server list', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[ADMIN], + post=[0x7939], + desc=''' + Server list and player count request. + ''', ) login_admin.s(0x7939, 'server list result', head=[ @@ -4064,14 +6151,24 @@ def build_context(): at(30, u16, 'is new'), ], repeat_size=32, + pre=[0x7938], + post=[IDLE], + desc=''' + Server list and player count response. + ''', ) - login_admin.r(0x793a, 'password check request', + login_admin.r(0x793a, 'password check', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_pass, 'password'), ], fixed_size=50, + pre=[ADMIN], + post=[0x793b], + desc=''' + Password check request. + ''', ) login_admin.s(0x793b, 'password check result', fixed=[ @@ -4080,14 +6177,24 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x793a], + post=[IDLE], + desc=''' + Password check response. + ''', ) - login_admin.r(0x793c, 'change sex request', + login_admin.r(0x793c, 'change sex', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, sex_char, 'sex'), ], fixed_size=27, + pre=[ADMIN], + post=[0x2723, 0x793d], + desc=''' + Modify sex request. + ''', ) login_admin.s(0x793d, 'change sex result', fixed=[ @@ -4096,14 +6203,24 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x793c], + post=[IDLE], + desc=''' + Modify sex response. + ''', ) - login_admin.r(0x793e, 'adjust gm level request', + login_admin.r(0x793e, 'adjust gm level', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, gm1, 'gm level'), ], fixed_size=27, + pre=[ADMIN], + post=[0x2732, 0x793f], + desc=''' + Modify GM level request. + ''', ) login_admin.s(0x793f, 'adjust gm level result', fixed=[ @@ -4112,14 +6229,24 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x793e], + post=[IDLE], + desc=''' + Modify GM level response. + ''', ) - login_admin.r(0x7940, 'change email request', + login_admin.r(0x7940, 'change email', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, account_email, 'email'), ], fixed_size=66, + pre=[ADMIN], + post=[0x7941], + desc=''' + Modify e-mail request. + ''', ) login_admin.s(0x7941, 'change email result', fixed=[ @@ -4128,9 +6255,14 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x7940], + post=[IDLE], + desc=''' + Modify e-mail response. + ''', ) # this packet is insane - login_admin.r(0x7942, 'change memo request', + login_admin.r(0x7942, 'change memo', head=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), @@ -4141,6 +6273,11 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[ADMIN], + post=[0x7943], + desc=''' + Modify memo request. + ''', ) login_admin.s(0x7943, 'change memo result', fixed=[ @@ -4149,13 +6286,23 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x7942], + post=[IDLE], + desc=''' + Modify memo response. + ''', ) - login_admin.r(0x7944, 'account id lookup request', + login_admin.r(0x7944, 'account id lookup', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), ], fixed_size=26, + pre=[ADMIN], + post=[0x7945], + desc=''' + Find account id request. + ''', ) login_admin.s(0x7945, 'account id lookup result', fixed=[ @@ -4164,13 +6311,23 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x7944], + post=[IDLE], + desc=''' + Find account id response. + ''', ) - login_admin.r(0x7946, 'account name lookup request', + login_admin.r(0x7946, 'account name lookup', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[ADMIN], + post=[0x7947], + desc=''' + Find account name request. + ''', ) login_admin.s(0x7947, 'account name lookup result', fixed=[ @@ -4179,31 +6336,24 @@ def build_context(): at(6, account_name, 'account name'), ], fixed_size=30, + pre=[0x7946], + post=[IDLE], + desc=''' + Find account name response. + ''', ) - login_admin.r(0x7948, 'validity absolute request', - fixed=[ - at(0, u16, 'packet id'), - at(2, account_name, 'account name'), - at(26, time32, 'valid until'), - ], - fixed_size=30, - ) - login_admin.s(0x7949, 'validity absolute result', - fixed=[ - at(0, u16, 'packet id'), - at(2, account_id, 'account id'), - at(6, account_name, 'account name'), - at(30, time32, 'valid until'), - ], - fixed_size=34, - ) - login_admin.r(0x794a, 'ban absolute request', + login_admin.r(0x794a, 'ban absolute', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, time32, 'ban until'), ], fixed_size=30, + pre=[ADMIN], + post=[0x2731, 0x794b], + desc=''' + Ban date end change request. + ''', ) login_admin.s(0x794b, 'ban absolute result', fixed=[ @@ -4213,14 +6363,24 @@ def build_context(): at(30, time32, 'ban until'), ], fixed_size=34, + pre=[0x794a], + post=[IDLE], + desc=''' + Ban date end change response. + ''', ) - login_admin.r(0x794c, 'ban relative request', + login_admin.r(0x794c, 'ban relative', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), at(26, human_time_diff, 'ban add'), ], fixed_size=38, + pre=[ADMIN], + post=[0x2731, 0x794d], + desc=''' + Ban date end change request (2). + ''', ) login_admin.s(0x794d, 'ban relative result', fixed=[ @@ -4230,9 +6390,14 @@ def build_context(): at(30, time32, 'ban until'), ], fixed_size=34, + pre=[0x794c], + post=[IDLE], + desc=''' + Ban date end change response (2). + ''', ) # evil packet (see also 0x2726) - login_admin.r(0x794e, 'broadcast message request', + login_admin.r(0x794e, 'broadcast message', head=[ at(0, u16, 'packet id'), at(2, u16, 'unused'), @@ -4243,6 +6408,11 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[ADMIN], + post=[0x2726, 0x794f], + desc=''' + Send broadcast message request. + ''', ) login_admin.s(0x794f, 'broadcast message result', fixed=[ @@ -4250,30 +6420,23 @@ def build_context(): at(2, u16, 'error'), ], fixed_size=4, + pre=[0x794e], + post=[IDLE], + desc=''' + Send broadcast message response. + ''', ) - login_admin.r(0x7950, 'validity relative request', - fixed=[ - at(0, u16, 'packet id'), - at(2, account_name, 'account name'), - at(26, human_time_diff, 'valid add'), - ], - fixed_size=38, - ) - login_admin.s(0x7951, 'validity relative result', - fixed=[ - at(0, u16, 'packet id'), - at(2, account_id, 'account id'), - at(6, account_name, 'account name'), - at(30, time32, 'valid until'), - ], - fixed_size=34, - ) - login_admin.r(0x7952, 'account name info request', + login_admin.r(0x7952, 'account name info', fixed=[ at(0, u16, 'packet id'), at(2, account_name, 'account name'), ], fixed_size=26, + pre=[ADMIN], + post=[0x7953], + desc=''' + Account information by name request. + ''', ) # this packet is insane login_admin.s(0x7953, 'account info result', @@ -4289,7 +6452,7 @@ def build_context(): at(60, millis, 'last login string'), at(84, str16, 'ip string'), at(100, account_email, 'email'), - at(140, time32, 'connect until'), + at(140, time32, 'unused connect until'), at(144, time32, 'ban until'), at(148, SkewLengthType(u16, 150), 'magic packet length'), ], @@ -4298,36 +6461,298 @@ def build_context(): at(0, u8, 'c'), ], repeat_size=1, + pre=[0x7952, 0x7954], + post=[IDLE], + desc=''' + Account information by name or id response. + ''', ) - login_admin.r(0x7954, 'account id info request', + login_admin.r(0x7954, 'account id info', fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), ], fixed_size=6, + pre=[ADMIN], + post=[0x7953], + desc=''' + Account information by id request. + ''', ) - login_admin.r(0x7955, 'reload gm signal', + login_admin.r(0x7955, 'reload gm', fixed=[ at(0, u16, 'packet id'), ], fixed_size=2, + pre=[ADMIN], + post=[0x2732], + desc=''' + Reload GM file request. + ''', ) + # TOC_NEW ## new-style packets - # general packets - any_user.x(0x8000, 'special hold', + # notify packets, standalone, can occur at any time; always 'payload' + any_user.s(0x8000, 'special hold notify', payload=[ at(0, u16, 'packet id'), # packet 0x8000 is handled specially at(2, u16, 'packet length'), ], payload_size=4, - ) + pre=[OTHER], + post=[OTHER], + desc=''' + A special packet that is handled specially. + + The "is the packet complete" logic will read its given length, + but the "skip packet" will only skip 4 bytes, thus allowing + transactions. I'm still not entirely convinced that this is a + good idea - perhaps explicit 'being transaction buffer' and + 'end transaction buffer' packets? Allow nesting? + ''', + ) + # invoke/return/error packets, threaded; always 'payload' + # invoke: 0x9000 + # return: 0xA000 + # error: 0xB000 + # invoke: 0x9001 + # return: 0xA001 + # error: 0xB001 + # ... or should it be implicit via a wrapper? + # + # add_call_packet(from=ANY, to=ANY, id='0x?001', name='version info' + # invoke_type=struct(), return_type=struct(), error_type=struct()) + # for that matter, should these things be read from separate files? + # still a lot of ambiguous stuff here ... + # + # no, merging is a bad idea at the low level + # ... but for doc it is ... + return ctx +# TOC_DRAWING + +def fix_sort(idlst): + idlst = set(idlst) + return (sorted([i for i in idlst if isinstance(i, SpecialEventOrigin)], key=lambda e: e.name) + + sorted([i for i in idlst if not isinstance(i, SpecialEventOrigin)])) + +def check(ctx): + d = {} + + for ch in ctx._channels: + for p in ch.packets: + id = p.id + if id in d: + print('packet 0x%04x duplicated (old=%r, new=%r)' % (id, d[id], p)) + d[id] = p + + for (id, packet) in sorted(d.items()): + if packet.pre != fix_sort(packet.pre): + print('packet 0x%04x pre is not sorted' % id) + for pre in packet.pre: + if isinstance(pre, SpecialEventOrigin): + continue + if pre not in d: + print('packet 0x%04x pre 0x%04x does not exist' % (id, pre)) + elif id not in d[pre].post and id not in d[pre].xpost: + print('packet 0x%04x pre 0x%04x does not have me in post or xpost' % (id, pre)) + if packet.post != fix_sort(packet.post): + print('packet 0x%04x post is not sorted' % id) + if packet.xpost != fix_sort(packet.xpost): + print('packet 0x%04x xpost is not sorted' % id) + if len(set(packet.post) | set(packet.xpost)) != len(packet.post) + len(packet.xpost): + print('packet 0x%04x post intersects xpost' % id) + for post in packet.post: + if isinstance(post, SpecialEventOrigin): + continue + if post not in d: + print('packet 0x%04x post 0x%04x does not exist' % (id, post)) + elif id not in d[post].pre: + print('packet 0x%04x post 0x%04x does not have me in pre' % (id, post)) + + return d + +def partition(d): + ''' given a directed graph in the form + {1: [2, 3], 2: [3, 4], 3: [], 4: [], 5: [6], 6: []} + return a list of sets of connected keys, in the form + [{1, 2, 3, 4}, {5, 6}] + ''' + leaders = {k: k for k in d} + + # this code looks nothing like what I was intending to write + changed = True + while changed: + changed = False + for k, vlist in d.items(): + if vlist: + m = min(leaders[v] for v in vlist) + if m < leaders[k]: + changed = True + leaders[k] = m + else: + m = leaders[k] + else: + m = leaders[k] + for v in vlist: + if m < leaders[v]: + changed = True + leaders[v] = m + + followers = {} + for k, v in leaders.items(): + followers.setdefault(v, []).append(k) + return [set(v) for v in followers.values()] + +def ids_only(vpost): + rv = [e for e in vpost if not isinstance(e, SpecialEventOrigin)] + if len(rv) == len(vpost): + rv = vpost + return rv + +def make_dots(ctx): + d = check(ctx) + # automatically excludes xpost and does not touch pre + # disabled because that's still just too many packets for the 0x0063 + #p = partition({k: ids_only(v.post) for (k, v) in d.items()}) + + for g in glob.glob('doc-gen/*.gv'): + os.rename(g, g + '.old') + for g in glob.glob('doc-gen/Packet-*.md'): + os.rename(g, g + '.old') + + for (id, p) in d.items(): + md = 'doc-gen/Packet-0x%04x.md' % id + dot = 'doc-gen/packets-around-0x%04x.gv' % id + with OpenWrite(md) as f: + f.write(p.wiki_doc()) + with OpenWrite(dot) as f: + # The goal looks sort of like this: + # + # O O O O + # \ / \ / + # O O + # \ / + # \ / + # O + # / \ + # / \ + # O O + # / \ / \ + # O O O O + # + # except that we also connect any extra connections between those + # in particular, this will DTRT for the common skip-case: + # + # | + # O + # |\ + # | O + # | | + # | O + # |/ + # O + # | + # + # regardless of which node you're drawing from. + # + # however, note that we do *not* list siblings. + # + # The above comment is wrong. + # Also, remember that none of this would be necessary without + # the overbroad dependencies! + # + # ... except that I got rid of the overbroad dependencies but + # had to kept this still. It's arguably nicer anyway, so I + # also made it the only way. + covered_nodes = sorted(p.pre_set(d, 2) | p.post_set(d, 2)) + covered_edges = [(a, b) for a in covered_nodes for b in covered_nodes if b in d[a].post] + g = Graph() + g.default_vertex[u'shape'] = u'box' + # g[u'layout'] = u'twopi' + # g[u'root'] = u'0x%04x' % id + for n in covered_nodes: + v = g.vertex(u'0x%04x' % n) + v[u'label'] = u'Packet \\N: %s' % d[n].name + if n == id: + v[u'style'] = u'filled' + for (a, b) in covered_edges: + g.edge(u'0x%04x' % a, u'0x%04x' % b) + for n in covered_nodes: + # the center node will be covered specially below + if n == id: + continue + # backward links (both strong and weak) + count = 0 + for p in d[n].pre: + # pre specials are always strong links + if isinstance(p, SpecialEventOrigin): + count += 1 + elif n in d[p].xpost: + # weak backward links + pass + elif p not in covered_nodes: + # don't show mere siblings unless also ancestor/descendent + count += 1 + if count: + v = g.vertex(u'0x%04x...pre' % n) + v[u'label'] = u'%d more' % count + v[u'style'] = u'dashed' + g.edge(v, u'0x%04x' % n) + # strong forward links + count = 0 + for p in d[n].post: + if isinstance(p, SpecialEventOrigin): + count += 1 + elif p not in covered_nodes: + count += 1 + if count: + v = g.vertex(u'0x%04x...post' % n) + v[u'label'] = u'%d more' % count + v[u'style'] = u'dashed' + g.edge(u'0x%04x' % n, v) + # for the immediate node, also cover specials and weaks + for p in d[id].pre: + # (there are no weak backward specials) + # strong backward specials + if isinstance(p, SpecialEventOrigin): + g.edge(unicode(p.name), u'0x%04x' % id) + # weak backward nodes + elif id in d[p].xpost: + e = g.edge(u'0x%04x' % p, u'0x%04x' % id) + e[u'style']=u'dashed' + e[u'weight'] = u'0' + for p in d[id].post: + # strong forward specials + if isinstance(p, SpecialEventOrigin): + g.edge(u'0x%04x' % id, unicode(p.name)) + for p in d[id].xpost: + # weak forward specials + if isinstance(p, SpecialEventOrigin): + e = g.edge(u'0x%04x' % id, unicode(p.name)) + e[u'style'] = u'dashed' + e[u'weight'] = u'0' + # weak forward nodes + elif p not in covered_nodes: + e = g.edge(u'0x%04x' % id, u'0x%04x' % p) + e[u'style'] = u'dashed' + e[u'weight'] = u'0' + g.dot(f, False) + + + for g in glob.glob('doc-gen/*.old'): + print('Obsolete: %s' % g) + os.remove(g) + +# TOC_MAIN + def main(): ctx = build_context() ## teardown + make_dots(ctx) ctx.dump() if __name__ == '__main__': -- cgit v1.2.3-60-g2f50