Skip to content
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

How to port the heroprotocol to Python 3 #57

Closed
Joko013 opened this issue Nov 10, 2017 · 17 comments · Fixed by #90
Closed

How to port the heroprotocol to Python 3 #57

Joko013 opened this issue Nov 10, 2017 · 17 comments · Fixed by #90
Assignees

Comments

@Joko013
Copy link

Joko013 commented Nov 10, 2017

Let me start with saying that I'm not super experienced with Python yet but I'm learning gradually. Can you please assist when I am trying to port the heroprotocol to Python 3? I installed the required library and when I try to run header = protocol.decode_replay_header(mpq.header['user_data_header']['content']) I get this error: TypeError: ord() expected string of length 1, but int found. The error says that it's in the decoders.py file on the row 69. Just to clarify, I am using latest Anaconda release. Thanks for any help.

@petronny
Copy link
Contributor

See #59

@Joko013
Copy link
Author

Joko013 commented Nov 21, 2017

Thanks.

I have a follow-up question. Is there any kind of wiki that would help me find the info I am looking for? I found this javascript port of heroprotocol that has some sort of documentation but it really isn't too deep.

My specific issue right now is how to find all deaths that happened over the course of the game and list them with player names, teams, etc. I would much rather dive in the file to try and find them myself but I don't really know where to start when I don't want just some basic info.

@yretenai
Copy link

Game Events hold most of that data i think, that or tracker events.

@Joko013
Copy link
Author

Joko013 commented Nov 22, 2017

Hm, I have these events in Game.Event
'NNet.Game.SCameraUpdateEvent', 'NNet.Game.SCmdEvent', 'NNet.Game.SCmdUpdateTargetPointEvent', 'NNet.Game.SCmdUpdateTargetUnitEvent', 'NNet.Game.SCommandManagerStateEvent', 'NNet.Game.SGameUserLeaveEvent', 'NNet.Game.SHeroTalentTreeSelectedEvent', 'NNet.Game.SSelectionDeltaEvent', 'NNet.Game.STriggerCutsceneEndSceneFiredEvent', 'NNet.Game.STriggerDialogControlEvent', 'NNet.Game.STriggerSoundLengthSyncEvent', 'NNet.Game.STriggerSoundOffsetEvent', 'NNet.Game.STriggerSoundtrackDoneEvent', 'NNet.Game.STriggerTransmissionCompleteEvent', 'NNet.Game.STriggerTransmissionOffsetEvent', 'NNet.Game.SUnitClickEvent', 'NNet.Game.SUserFinishedLoadingSyncEvent', 'NNet.Game.SUserOptionsEvent'

and these in Tracker.Event
NNet.Replay.Tracker.SPlayerSetupEvent', 'NNet.Replay.Tracker.SScoreResultEvent', 'NNet.Replay.Tracker.SStatGameEvent', 'NNet.Replay.Tracker.SUnitBornEvent', 'NNet.Replay.Tracker.SUnitDiedEvent', 'NNet.Replay.Tracker.SUnitOwnerChangeEvent', 'NNet.Replay.Tracker.SUnitPositionsEvent', 'NNet.Replay.Tracker.SUnitRevivedEvent', 'NNet.Replay.Tracker.SUnitTypeChangeEvent', 'NNet.Replay.Tracker.SUpgradeEvent'

Do you know where a death event could be? I can't seem to find it.

@yretenai
Copy link

yretenai commented Nov 22, 2017

SUnitDiedEvent, heroes are units.

@barrett777
Copy link

If you are comfortable with C#, you can use this with friendlier objects: https://github.com/barrett777/Heroes.ReplayParser/

@Joko013
Copy link
Author

Joko013 commented Nov 23, 2017

@NaomiChan Of course, thanks.
@barrett777 Hi Ben, unfortunately I'm not. But since you've already done so much with the replays, don't you have some notes about events that would help understand what is what/where etc.? Right now, I need just the deaths but I'm fairly certain that I'm going to dive deeper into the file later.

@ajbdev
Copy link

ajbdev commented Nov 23, 2017

It would be nice if we had a repo dedicated just to annotating / documenting the replay file format. It seems like we're all independently learning it on our own for the most part and I think we'd end up saving each other a lot of time if we had a source of knowledge for this where we could share what we know. This would also probably make it easier to recognize if/when something has changed about the format.

I'm not exactly sure what the best format for this documentation would be. If anyone has any ideas I'd be happy to take the initiative to create and/or contribute to this.

@Joko013
Copy link
Author

Joko013 commented Nov 23, 2017

You are 100% correct! So far I found only this documentation that is for JS protocol and has some info. But it's true that all of us could make use of a repo instead of figuring it out every time by ourselves.

I think Ben could have the most info since he's done the most work with replays of all of us. But it's just a guess and I don't know if he wants to share it in case he has it.

@Joko013
Copy link
Author

Joko013 commented Nov 23, 2017

I have another question. In my quest to find out which hero died when I tried to combine SUnitDiedEvent and SUnitBornEvent to get the name of the unit dying; like this (it's really an ugly way but I didn't think of any better way to match the Tags to get the name):

for event in (events for events in tracker_events if events['_event'] == NNet.Replay.Tracker.SUnitDiedEvent'):
    for tr_event in (events2 for events2 in tracker_events2 if events2['_event'] == NNet.Replay.Tracker.SUnitBornEvent'):
        if event['m_unitTagIndex'] == tr_event['m_unitTagIndex'] and \
         event['m_unitTagRecycle'] == tr_event['m_unitTagRecycle']:
            print tr_event['m_unitTypeName']

But for some reason the only result I get out of this is: Hell_GiantDemon_Base Which I have no idea what is and also is not the result I was hoping for. Can anyone see my mistake?

Also jupyter notebook sort of doesn't work when I try to loop through the tracker_events object for a second time in one run but that's a whole other issue (that's why there are two tracker_events objects).

@regner
Copy link
Contributor

regner commented Nov 23, 2017

@Joko013 FYI you can put ``` around multiline code to make it more readable on GH:

for event in (events for events in tracker_events if events['_event'] == 'NNet.Replay.Tracker.SUnitDiedEvent'):
    for tr_event in (events2 for events2 in tracker_events2 if events2['_event'] == 'NNet.Replay.Tracker.SUnitBornEvent'):
        if event['m_unitTagIndex'] == tr_event['m_unitTagIndex'] and event['m_unitTagRecycle'] == tr_event['m_unitTagRecycle']:
            print tr_event['m_unitTypeName']

Also you you specify a language after the first ``` GH will style the code accordingly.

@barrett777
Copy link

Tracker events have probably 95% of the things you are interested in

I only use game events for a handful of things

Because of how unit tags recycle, you need to process all unit related events. Or at least all but UnitPositionsEvent. Here's where I loop through these in C#, you might be able to figure out what I'm doing even though it's C#.
I do have some comments you can read through: https://github.com/barrett777/Heroes.ReplayParser/blob/master/Heroes.ReplayParser/Unit.cs#L460-L573

I loop through these events in chronological order: UnitBornEvent , UnitRevivedEvent, UnitDiedEvent, UnitOwnerChangeEvent, UnitPositionsEvent

'UnitBornEvent' has the name of the unit. I believe all hero units start with 'Hero'

'UnitDiedEvent' shows what other unit got the killing blow

A lot of my code in that file is also using game events to supplement the position data available. In tracker events, unit positions are only reported about once every 15 seconds I believe, so if you are wanting positions more frequently, you can piece together more from game events such as move commands.

@Joko013
Copy link
Author

Joko013 commented Nov 29, 2017

@barrett777 Thanks Ben, I think I'm slowly getting to the results I want. I have one more question though: Where do you get the TimeSpan of an event? I don't see such parameter when looking at the info that is contained in events.

To be clear right now I think I can get the Born/Died events correctly (still a little puzzled by how exactly does the Replay recycle the Ids) but I don't know how to determine when exactly an event occured.

@barrett777
Copy link

barrett777 commented Nov 29, 2017 via email

@Joko013
Copy link
Author

Joko013 commented Dec 3, 2017

Found it and it works just as you said, thanks Ben.

If anyone is interested, for tracker_events you can find the time when an event occured (0 is when the match starts, so 35 sec before the games open) here:

seconds_from_match_start = event['_gameloop']/16

@ajbdev
Copy link

ajbdev commented Jan 17, 2018

If anyone is interested, I whipped together an (incomplete) site that is starting to document the protocol and how different data can be retrieved from it.

You can find it here: https://nexus-devtools.github.io/heroprotocoldocs/#/

Feel free to PR any changes you think would help.

Agilhardt added a commit that referenced this issue Apr 16, 2020
Agilhardt added a commit that referenced this issue Apr 16, 2020
With this, the tool supports Python 3. Addresses issue #57.
Agilhardt added a commit that referenced this issue Apr 16, 2020
Agilhardt added a commit that referenced this issue Apr 16, 2020
With this, the tool supports Python 3. Addresses issue #57.
@Agilhardt Agilhardt self-assigned this Apr 17, 2020
@Agilhardt
Copy link
Collaborator

Fixed in the next update

Agilhardt added a commit that referenced this issue Jun 1, 2020
Python 3, pypi package, and module support

* Updates to support both Python 2.7 and Python 3.x
* Package now publishes to PyPI (https://pypi.org/project/heroprotocol)
* New method of invoke, instead of `python heroprotocol.py [...]`, run `python -m heroprotocol [...]`. This works either with the PyPI package installed, or running from source at the checkout root.

Resolves #4, resolves #5, resolves #57, resolves #59, resolves #85, resolves #86, resolves #88.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment