New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MOAR Slash Commands, Titles, AFKs, ChatMessages, String Replacements, and other unlabeled-bits #175

Merged
merged 33 commits into from Jan 23, 2018

Conversation

Projects
None yet
3 participants
@broxen
Collaborator

broxen commented Dec 4, 2017

Many small change in this one. Just trying to move the ball forward.

Additions/modifications proposed in this pull request:

  • Attempting to track down unlabeled bits and their purpose.
  • Added slash commands for fly, dazed, jumppack, jumpheight, and backupspd, AFK, plus more.
  • JumpHeight might work in tandem with u3 to enable "superjump" powers.
  • Added ChatMessage for Request, Friends, Group, Supergroup, and associated Slash commands
  • Pulled description and battle_cry out of entity. Fixed them so they update in your ID card properly. yay!
  • Added handler for selecting different chat channels from the chat window input box. code 37
  • TODO: still need to save things to the database, Titles, Desc, Battle_Cry, MapName, etc.
  • TODO: Jumppack and Dazed still need their associated costume parts and special FX (stars above head) respectively.
  • TODO: tried to add handler for client message 32, but it doesn't work, maybe I'm missing something?

The following slash commands now work in the client:

/f
/req
/sg
/g
/fly
/dazed
/jumppack
/setJumpHeight [float]
/setBackupspd [float]
/afk [msg]
/setTitles [the_flag] [generic] [origin] [special]
/setu1 [int]
/setu2 [int]
/setu3 [int]
/setu4 [int]
/setu5 [int]
/setu6 [int]

There are still many unknown bits here, but I have suspicions about these two in SendServerControlState()

        bs.StoreBits(1,u2);     // if 1/true entity anims stop, can still move, but camera stays. Slipping on ice?
        bs.StoreBits(1,u3);     // leaping? seems like the anim changes slightly?

EDIT: Typos.

@broxen

This comment has been minimized.

Collaborator

broxen commented Dec 4, 2017

Oh neato. It carries my own comments over.

Well the crashing issues are fixed, but I always encourage testing. Also, please feel free to let me know if there are better ways of achieving my goals, and if you know what any of these unknown bits do.

@nemerle

This comment has been minimized.

Contributor

nemerle commented Dec 4, 2017

This looks great.

Tomorrow should be the last day of that big at-work thing I'm currently busy with, so the day after I'l look through my resources for all those u1/u2 etc. bits and pieces and merge this

@mobbyg

This comment has been minimized.

Collaborator

mobbyg commented Dec 4, 2017

Hey guys, thank you for the work you’ve done on this project. It’s really great reading the emails for the commits. I try and update server as soon as you post changes so I can offer any info I can, as I don’t know C++, but I have a For Dummies book now. 😜

@broxen

This comment has been minimized.

Collaborator

broxen commented Dec 5, 2017

Great. Thank you both!

When you get a chance to look this over, can you tell me how I can call toggleAFK() when a player moves? Seems like it should go in sendServerControlState() but I can't modify Entity there because it's const (and rightly so)

Intended behavior is that movement (WASD, or mouse) will turn off AFK flag.

@broxen

This comment has been minimized.

Collaborator

broxen commented Dec 6, 2017

Hope you don't mind me pushing another update to this PR.

  • Added more slash commands.
  • Also, I realized that I need to set m_afk to false on movement, not Toggle. So if you can point me in the right direction for that, I'd appreciate it.
  • Identified and began framework for opcodes 2, 8, 18, 29
  • Also fixed opcode 16, which was somehow removed from MapEventFactory.cpp
void Character::setTitles(bool prefix, QString generic, QString origin, QString special)
{
if(!prefix && generic.isEmpty() && origin.isEmpty() && special.isEmpty())

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

consider writing this like ?

m_has_titles = prefix || !generic.isEmpty() || !origin.isEmpty() || special.isEmpty();
if(!m_has_titles)
  return;

This comment has been minimized.

@broxen

broxen Dec 6, 2017

Collaborator

Ooo. Much cleaner!

void Character::toggleAFK(const QString &msg)
{
m_afk = !m_afk;
m_afk_msg = msg;

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

Do we need to clean up m_afk_msg if client is no longer afk ?

This comment has been minimized.

@broxen

broxen Dec 6, 2017

Collaborator

Testing this: no, it doesn't seem like it matters. But my (admittedly terrible) memory tells me that the official server behavior was actually to save this msg between uses (during the same connection)

I think that because I used to go afk with msg, move (removing afk flag) and then set afk flag again without msg and it would have my old msg left in the system (probably client side memory).

I guess I could achieve this with a simple if statement, but is there any reason (memory) to clear this value instead?

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

Nope, I've only mentioned this since it seemed 'logical', but since the original server was doing it the other way, You should leave it as is :)

QString m_character_description;
bool m_afk = false;
QString m_afk_msg;
// Experience Requirements Chart. Same for all classes.

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

We should load it from the data files anyway :)
If everything goes right today, I'll be pushing code to access those kinds of things.

This comment has been minimized.

@broxen

broxen Dec 6, 2017

Collaborator

I looked in attribs (classes.bin) and didn't see this xp chart anywhere. Is it in another file?

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

Huh, You're right, I'd swear I've seen the exp table somewhere

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

Yup, it's in experience.bin added issues #176

This comment has been minimized.

@broxen

broxen Dec 6, 2017

Collaborator

Seems like it would be, there's no real harm in exposing this info client side. And, you've looked through more of the bin files than me, so I'm sure you're right, it's there somewhere.

void serializefrom(BitStream &bs) override
{
entity_idx = bs.GetPackedBits(12);
boost_unk1 = bs.GetAvailSize(); // How many bits!?

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

please mark things like this with

//TODO:

Qtcreator has a very nice plugin that can list allo TODO: / BUG: etc. markers in the project :)

This comment has been minimized.

@broxen

broxen Dec 6, 2017

Collaborator

Oh neato! I'll be more consistent with this then.

msg.startsWith("req ") || msg.startsWith("request ") || msg.startsWith("auction ") || msg.startsWith("sell ") ||
msg.startsWith("f ") || msg.startsWith("friends ") ||
msg.startsWith("t ") || msg.startsWith("tell ") || msg.startsWith("w ") || msg.startsWith("whisper ") ||
msg.startsWith("p ") || msg.startsWith("private ");
}

This comment has been minimized.

@nemerle

nemerle Dec 6, 2017

Contributor

Maybe we could simplify this by using QStringList::contains here ?

static const QStringList chat_prefixes = { "l","local","b","broadcast",/* the rest would follow */ };
QString prefix(msg.mid(0,indexOf(' '));
return chat_prefixes.contains(prefix);

This comment has been minimized.

@broxen

broxen Dec 6, 2017

Collaborator

Thank you! I was struggling to figure out a better way to do this, and my brain just wasn't going anywhere.

@broxen

This comment has been minimized.

Collaborator

broxen commented Dec 6, 2017

Updated based upon feedback, and added additional TODO comments and cleaned up the comments overall.

Also, I cleaned up the code a tiny bit.

EVENT_DECL(evInspirationDockMode ,217)
EVENT_DECL(evPowersDockMode ,218)
EVENT_DECL(evUseInspiration ,229) // Hit F1, F2, F3, F4, F5 or Click on Inspiration in Tray
EVENT_DECL(evSetTarget ,232) // Click on another entity

This comment has been minimized.

@nemerle

nemerle Dec 7, 2017

Contributor

I'm almost 100% positive that command 32 is related to unqueing a power or inspiration execution:
Take a look at client code:

void sendPacket32(Entity *a1)
{
    my_pakSendPackedBits(g_input_pak, 1, 32);
}
void  AbortQueuedPower()
{
    Entity *e = getControlledPlayerEntity();
    sendPacket32(e);
    inspirationUnqueue();
    g_cursor.target_world = 0;
    s_ptsActivated = nullptr;
}

This comment has been minimized.

@broxen

broxen Dec 7, 2017

Collaborator

I actually read this a different way, informed by the client behavior during testing.

I think command 32 is still related to targeting, as we already have EVENT_DECL(evAbortQueuedPower ,233) which fires when you hit 'Z' (bound to unqueue powers by default). Presumably g_input_pak contains the entity you're targeting Entity *a1

But I think AbortQueuedPower is a function called when you hit ESC and is not related to command 32, but simply calls it's send function.

This would make sense because hitting ESC should do all of the following:

  1. set target to self
    Entity *e = getControlledPlayerEntity();
    sendPacket32(e);
  1. unqueue any powers or inspirations you've activated inspirationUnqueue();
  2. clear any active waypoints/markers on the map (presumably) s_ptsActivated = nullptr;

I can't speak to the purpose of g_cursor.target_world = 0; maybe it's just clarifying that you're not targeting any game world objects (desks, etc?)

This comment has been minimized.

@broxen

broxen Dec 7, 2017

Collaborator

latest push appears to correct this behavior here, and supports my supposition in that hitting ESC triggers both SetTarget and AbortQueuedPower resolving the issue in the comments of #174

{
entity_idx = bs.GetPackedBits(12);
boost_unk1 = bs.GetAvailSize(); // TODO: Not all bits were consumed
}

This comment has been minimized.

@nemerle

nemerle Dec 7, 2017

Contributor

Packet 29 - activate inspiration slot ( it does not contain entity_idx)
packedbits(1,29);
packedbits(3,slot_idx);
packedbits(3,row_idx);

@@ -454,7 +508,52 @@ class ChatReconfigure final : public MapLinkEvent
m_chat_bottom_flags = bs.GetPackedBits(1);
}
};
class PowersDockMode final : public MapLinkEvent

This comment has been minimized.

@nemerle

nemerle Dec 7, 2017

Contributor

Packet 18 is used when a secondary tray is toggled:

  • packedBits(1,18)
  • bits(1,toggle_secondary_tray)

This comment has been minimized.

@broxen

broxen Dec 8, 2017

Collaborator

This comment doesn't show as cleared, but is resolved in the latest push.

// TODO: Not all bits were consumed
}
};
class SwitchTray final : public MapLinkEvent

This comment has been minimized.

@nemerle

nemerle Dec 7, 2017

Contributor

Packet 8 contains serialized PowerTrayGroup

bs.StoreBits(1,m_has_the_prefix); // likely an index to a title prefix ( 0 - None; 1 - The )
storeStringConditional(bs, m_titles[0]); // Title 1 - generic title (first)
storeStringConditional(bs, m_titles[1]); // Title 2 - origin title (second)
storeStringConditional(bs, m_titles[2]); // Title 3 - yellow title (special)
}

This comment has been minimized.

@nemerle

nemerle Dec 8, 2017

Contributor

Sadly this is incorrect vs. the client code:

void  receiveTitles(DbPacket *pak)
{
    Entity *e = my_getControlledPlayerEntity();
    e->ent_player2->titleThe = my_pakGetBits(pak, 1);
    strncpy(e->ent_player2->Title1, my_pakGetString(pak), 32);
    strncpy(e->ent_player2->Title2, my_pakGetString(pak), 32);
    strncpy(e->ent_player2->Title3, my_pakGetString(pak), 128);
}

So if You had to convert it to conditional strings, then we have a problem somewhere before the sendTitles

This comment has been minimized.

@broxen

broxen Dec 8, 2017

Collaborator

Ah, it just clicked for me how storeStringConditonal actually works.

Not sure where I got the idea to use this, but I can confirm that /setTitles bFlag generic origin special actually works.

Can also confirm that prior to using Conditional, the client would crash sporadically and this was my best fix.

Maybe the client expects a has_titles flag before this group is received?

EDIT: consolidated comments for clarity.

This comment has been minimized.

@broxen

broxen Dec 10, 2017

Collaborator

I pulled this from sendTitles in Entity (which is duplicitous and I have now removed)

After the changes in my latest push, I tried to send bs.StoreString() instead, but it still crashes the client =/

Any ideas?

This comment has been minimized.

@broxen

broxen Dec 11, 2017

Collaborator

Also, why e->ent_player2 instead of player1

bs.StoreBits(1,m_is_stunned); // is_stunned flag (lacks overhead 'dizzy' FX)
bs.StoreBits(1,m_has_jumppack); // jumpack flag (lacks costume parts)
bs.StoreBits(1,u2); // if 1/true entity anims stop, can still move, but camera stays. Slipping on ice?

This comment has been minimized.

@nemerle

nemerle Dec 8, 2017

Contributor

this is likely "controls disabled" bit

This comment has been minimized.

@broxen

broxen Dec 8, 2017

Collaborator

That makes sense. Though the entity can still move?

broxen added some commits Dec 2, 2017

Tracking down un-labeled bits and their purpose. Added slash commands…
… for fly, dazed, jumpheight, and backupspd, plus more.
MOAR ChatMessages, Slash Commands, Titles, AFKs, and borked stuff.
- Added ChatMessage handlers for various types
- Added Slash Commands to set Titles, AFK status, and more
- Added afk toggle and away message
- Moved description and battle_cry to Character class, and borked stuff 😭
Added MOAR slash commands, setup framework for Private Messages, Work…
…ed on Unhandled Ev's

- added /whoall
- added /setXP
- added /setLevel
- added /setHP
- added /setEnd
- added /t framework, TODO: needs target idx
- added framework for SwitchTray, PowersDockMode, SetTarget
- added m_experience_reqs array for min-xp per level

 #
Updated MapInstance and MapEvents.h based upon feedback.
- Fixed command 32, 29, 18
- Renamed variables based upon #177
- Fixed MapEvent 238 and tested
- Created variables for m_cur_chat_channel and m_cur_target (future use!)
- Renamed UseInspiration to ActivateInspiration
- TODO: command 32 returns -1?
- TODO: command 8 power tray serialization
- TODO: Leverage experience.bin serialization for xp reqs table
@broxen

This comment has been minimized.

Collaborator

broxen commented Dec 8, 2017

I'll try to rebase this and resolve those conflicts in EntitiesResponse.cpp late tonight.

@broxen

This comment has been minimized.

Collaborator

broxen commented Dec 9, 2017

Note that this update_part_2 section is completely wrong. Setting update_part_2 flag to true results in wild behavior, including flashing of location and the entity being relocated to /loc 3.0 0.0 3.0

Maybe this section has to do with teleporting?

  • m_if_teleporting
  • set client pos id (maybe a reference to current location?)
  • speed? maybe actually pos, since currently it sends you to 3.0 0.0 3.0
  • zeroes? maybe flags for FX? maybe a point in space to direct you to for teleport?
  • set orientation upon arrival
  • no idea on u6?
    // Used to force the client to a position/speed/pitch/rotation by server
    bs.StoreBits(1,update_part_2);
    if(update_part_2)
    {
        bs.StorePackedBits(1,u5);            // sets g_client_pos_id_rel default = 0
        storeVector(bs,spd);
        storeVectorConditional(bs,zeroes);      // vector3 -> speed ? likely; was zeroes

        storeFloatConditional(bs,0);         // Pitch not used ?
        storeFloatConditional(bs,ent->inp_state.camera_pyr.y); // Pitch
        storeFloatConditional(bs,0);         // Roll
        bs.StorePackedBits(1,u6);            // sets the lowest bit in CscCommon::flags default = 0
    }

@broxen broxen force-pushed the broxen:unlabeled-bits branch from 04b13f3 to 4ad1c34 Dec 10, 2017

More slash commands, add fields to DB, replacement strings for chat, …
…and more

- Added replacement strings for chat
- Pulled experience table for setXP() from serialized bin data.
- /lfg
- /setInf num
- /set
- Filled some data from the DB upon character load
- Rebased and resolved conflicts in EntitiesResponse.cpp
- Added many TODO comments
"title=:title, badgetitle=:badgetitle, "
"specialtitle=:specialtitle, supergroup_id=:supergroup_id, "
"options=:options, gui=:gui "
"WHERE id=:id ");

This comment has been minimized.

@nemerle

nemerle Jan 10, 2018

Contributor

Many of those fields should be serialized into CharacterData blob

":title, :badgetitle, :specialtitle, :supergroup_id, :options, :gui"
*/
m_prepared_char_insert.bindValue(":char_level", c->m_level);

This comment has been minimized.

@nemerle

nemerle Jan 10, 2018

Contributor
#include <iostream>
#include <cereal/archives/json.hpp>
#include <cereal/types/vector.hpp>
//......
  std::stringstream ss;
  {
    cereal::JSONOutputArchive archive( ss );
    CharacterData charData;
    archive( charData );
  }
  m_prepared_char_insert.bindValue(":char_data", QByteArray::fromStdString(charData.str()));

This comment has been minimized.

@broxen

broxen Jan 11, 2018

Collaborator

I'm trying to look at costume parts for inspiration here, but I'm failing to understand this. Can you point me towards an example?

I know that I need:

  • new CharacterData class (or struct as Char class member?) containing hp, end, m_current_attribs and m_max_attribs etc.
  • new serializefromdb() and serializetodb() methods for serializing these values. I see an example in Costume.cpp for parts.

This comment has been minimized.

@nemerle

nemerle Jan 11, 2018

Contributor

An example from anim_serializers.cpp

template<class Archive>
void serialize(Archive & archive, TextureAnim_Data & m)
{
    archive(cereal::make_nvp("Speed",m.speed));
    archive(cereal::make_nvp("Scale",m.stScale));
    archive(cereal::make_nvp("Type",m.scrollType));
    archive(cereal::make_nvp("flags",m.flags));
}

Then you can create

void serializeFromDb(CharacterData &data,const QByteArray &src)
{
    if(src.isEmpty())
        return;
    std::istringstream istr;
    istr.str(src.toStdString());
    {
        cereal::JSONInputArchive ar(istr);
        ar(data);
    }
}

There are a few cereal features we're going to use ( class versioning, different archive formats ), so the code above will have to handle some additional logic, but this is a basic example :)

if(!doIt(m_prepared_char_insert))
return false;
int64_t char_id = m_prepared_char_insert.lastInsertId().toLongLong();
c->m_db_id = char_id;

This comment has been minimized.

@broxen

broxen Jan 11, 2018

Collaborator

Here I'm trying to pass the Character Table row uid to c->m_db_id. Later I try setting c->m_db_id to e->m_db_id.

This comment has been minimized.

@broxen

broxen Jan 11, 2018

Collaborator

this now works as intended

if(!doIt(m_prepared_costume_insert))
return false;
return true;
}
bool CharacterDatabase::update( Character *c )

This comment has been minimized.

@broxen

broxen Jan 11, 2018

Collaborator

This still doesn't run and I believe it's because c->m_db_id (:id) is never defined. It's always 0.

This comment has been minimized.

@broxen

broxen Jan 11, 2018

Collaborator

this works now, as expected

if(e->m_type==Entity::ENT_PLAYER)
{
CharacterDatabase *char_db = AdminServer::instance()->character_db();
char_db->update(&e->m_char);

This comment has been minimized.

@broxen

broxen Jan 11, 2018

Collaborator

Is this the best place to put this?

This comment has been minimized.

@nemerle

nemerle Jan 11, 2018

Contributor

The best ? no. What we should do is a 'service thread' that periodically runs over all chars, and stores their state into db, what w should do in here, is mark the character state as 'dirty'.

But this will work for now ( add a ticket to fix/design a saner db-update code ) :)

This comment has been minimized.

@broxen

broxen Jan 13, 2018

Collaborator

Actually this will not work for now. When more than one client is connected it crashes the server. My guess is that it tries to write to the dbase simultaneously?

Fixed Char DB update() method and added chardebug and updatechar slas…
…h commands

Worked on getEntity utility methods
@nemerle

This comment has been minimized.

Contributor

nemerle commented on Projects/CoX/Servers/MapServer/MapServer.cpp in bcec17d Jan 11, 2018

hmmm.. this looks strange, pEnt is local variable, thus it will be 'dead' after return, thus this assign is no-op

return &e;

will work

@nemerle

This comment has been minimized.

Contributor

nemerle commented on Projects/CoX/Servers/MapServer/MapServer.cpp in bcec17d Jan 11, 2018

this else should be after the loop, reporting only in case no entity was matched, as it stands now, it will report after every non-matched entity :)

broxen added some commits Jan 16, 2018

Fixed $target and added /stuck, /hascontrolid & /fullupdate commands
Started framework for CharData class and serialization.
@broxen

This comment has been minimized.

Collaborator

broxen commented Jan 16, 2018

Current status of this PR:

  • /updatechar works and updates character in database, but I cannot call CharacterDatabase::update() in WorldSimulation.cpp or the server will freeze when multiple clients are attached. 🤷‍♂️
  • started fleshing out a CharacterData class based upon anim_serialization. I'm not confident that I'm heading in the right direction though. Maybe it's better to just build off of the existing Character class instead?
  • getEntity() works but will not function from different map instances and still relies on the EntityManager object created by MapInstance (or MapClient). For now this should work fine, as we only have one instance, but in the future we'll need a better way!

TODO:

  • If I can get some help figuring out how to call getEntity() from the CharacterDatabase::update() method, I can save position information to the database. First steps towards persistence!
  • May I please have some additional guidance on the character stats serialization? Am I moving in the right direction?

Once those two items are resolved, this should be ready for commit.

@broxen

This comment has been minimized.

Collaborator

broxen commented Jan 19, 2018

Identified a regression disabling entity rotation caused by converting m_orientation_pyr from float to vec3. But rotation is still choppy. Not sure how to speed up those updates.

broxen added some commits Jan 22, 2018

Continued separation of member variables from Character and Entity cl…
…asses

Added additional getter/setter functions to PlayerMethods

TODO: Getting a linker error for playermethod functions. WHYYyyyyyyYY?
Fixed Linking Error and added nullptr checks for /getEntity
Also started some prelim database update work for serialization
@broxen

This comment has been minimized.

Collaborator

broxen commented Jan 22, 2018

This compiles cleanly and everything seems to work. I think it's ready to commit with the following notices:

  • I'm stuck on serializing to the database, I'm looking to Costume update, but missing something. So I think I've serialized everything in Character and Entity classes, but we're not currently doing anything with that JSON data, and frankly, I'm not sure how to call it elsewhere.
  • I was unable to pull m_target_idx out of inp_state due to scope. InputStorage is unaware of the current entity.
  • I am unable to run CharacterDatabase::update() during World::updateEntity() because it freezes the clients with more than one character logged in.
  • While we can update to the database using /updatechar, there's currently no way to fill entity data from the database (pos, orientation_pyr) as the logic calling CharacterDatabase::fill() is more complex and unaware of the Entity owning the Character.

There's still plenty to do, but I think this PR is "complete" and achieved it's original goals (and then some). Let me know if you'd like a complete list of changes.

@nemerle nemerle merged commit 3ea5612 into Segs:master Jan 23, 2018

1 of 2 checks passed

continuous-integration/travis-ci/pr The Travis CI build failed
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details

@broxen broxen deleted the broxen:unlabeled-bits branch Jan 23, 2018

@broxen broxen referenced this pull request Feb 2, 2018

Closed

Chatting features #41

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment