Repository: markus-wa/demoinfocs-golang
Pull request #168 (2019-12-04 14:33:16)

add IParticipants.AllByUserID() method (#167)
New method `IParticipants.AllByUserID()` gives read-only access to the all current players (including spectators etc.) by user id.

deepcode-ci-bot[bot]: [**DeepCode Report (#74f096)**](https://www.deepcode.ai/app/gh/markus-wa/demoinfocs-golang/3643290ec1be9e05fd743d09310bbe50595b290c/markus-wa/demoinfocs-golang/74f096dec9dea312a25b7eb8d0472ffdbc988f0d/_/%2F/code/)
---
DeepCode analyzed this pull request.
There are no new issues. 
deepcode-ci-bot[bot]: [**DeepCode Report (#74f096)**](https://www.deepcode.ai/app/gh/markus-wa/demoinfocs-golang/3643290ec1be9e05fd743d09310bbe50595b290c/markus-wa/demoinfocs-golang/74f096dec9dea312a25b7eb8d0472ffdbc988f0d/_/%2F/code/)
---
DeepCode analyzed this pull request.
There are no new issues. 
codeclimate[bot]: Code Climate has analyzed commit 74f096de and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/markus-wa/demoinfocs-golang/pull/168).

markus-wa: Retry DeepCode
codeclimate[bot]: Code Climate has analyzed commit 1e778e7f and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/markus-wa/demoinfocs-golang/pull/168).

BestAwperEver: I don't really understand why Travis CI build is failing here :( seems like all tests passed but "check-interfaces-generated.sh" is returning "ERROR: generated code is not up-to-date" and points to the changes in the `IParticipants` interface I made. What does it mean?
markus-wa: This should explain it 🙂: https://github.com/markus-wa/demoinfocs-golang/blob/master/README.md#generating-interfaces
BestAwperEver: So you are using third-party util to generate interfaces for structs -- that's ok, but I still don't understand why build is failing 😞
I manually updated interface and both structures which were implementing that interface before I touched the code in a way that they both are still implementing it after changes were made. So what's wrong and what should be done for build to be successful?
markus-wa: You'll need to follow the instructions in the link to update the interface for `IParticipants` with `bin/generate-interfaces.sh`.
Then you need to commit the generated changes.

The reason we need to commit generated stuff is because otherwise the lib won't be go-gettable 🙂
codeclimate[bot]: Code Climate has analyzed commit 9d1d58df and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/markus-wa/demoinfocs-golang/pull/168).

BestAwperEver: Well, shame on me -- I didn't notice `// DO NOT EDIT: Auto generated` at the top of the file.
Couldn't manage to run the script on Windows:
```
d:\Documents\GitHub\demoinfocs-golang-fork>go generate
2019/12/05 13:46:53 63:1: comment not terminated (and 1 more errors)
parser.go:18: running "ifacemaker": exit status 1

d:\Documents\GitHub\demoinfocs-golang-fork>go generate ./sendtables
2019/12/05 13:46:54 23:1: comment not terminated (and 1 more errors)
sendtables\entity.go:13: running "ifacemaker": exit status 1
```
But looks like running
```
//go:generate ifacemaker -f game_state.go -s Participants -i IParticipants -p demoinfocs -D -y "IParticipants is an auto-generated interface for Participants." -c "DO NOT EDIT: Auto generated" -o participants_interface.go
```
from Goland (hopefully, since the build is still in progress) did the trick for me 😃 
codecov[bot]: # [Codecov](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168?src=pr&el=h1) Report
> Merging [#168](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168?src=pr&el=desc) into [master](https://codecov.io/gh/markus-wa/demoinfocs-golang/commit/3643290ec1be9e05fd743d09310bbe50595b290c?src=pr&el=desc) will **decrease** coverage by `0.17%`.
> The diff coverage is `20%`.

[![Impacted file tree graph](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168/graphs/tree.svg?width=650&token=20emCaZRHz&height=150&src=pr)](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #168      +/-   ##
==========================================
- Coverage    91.8%   91.62%   -0.18%     
==========================================
  Files          29       29              
  Lines        2586     2591       +5     
==========================================
  Hits         2374     2374              
- Misses        159      164       +5     
  Partials       53       53
```


| [Impacted Files](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [game\_state.go](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168/diff?src=pr&el=tree#diff-Z2FtZV9zdGF0ZS5nbw==) | `84.4% <20%> (-4.06%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168?src=pr&el=footer). Last update [3643290...9d1d58d](https://codecov.io/gh/markus-wa/demoinfocs-golang/pull/168?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codeclimate[bot]: Code Climate has analyzed commit 87caee2c and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/markus-wa/demoinfocs-golang/pull/168).

markus-wa: > Couldn't manage to run the script on Windows:

I've also had issues in the past when trying to run `go generate` on windows. Depending on how it's run it sometimes works (git bash vs cmd vs powershell vs ???)
codeclimate[bot]: Code Climate has analyzed commit bb27d225 and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/markus-wa/demoinfocs-golang/pull/168).


Patch:
diff --git a/fake/participants.go b/fake/participants.go
index 68137ba..2e4d662 100644
--- a/fake/participants.go
+++ b/fake/participants.go
@@ -24,6 +24,11 @@ func (ptcp *Participants) ByEntityID() map[int]*common.Player {
 	return ptcp.Called().Get(0).(map[int]*common.Player)
 }
 
+// AllByUserID is a mock-implementation of IParticipants.AllByUserID().
+func (ptcp *Participants) AllByUserID() map[int]*common.Player {
+	return ptcp.Called().Get(0).(map[int]*common.Player)
+}
+
 // All is a mock-implementation of IParticipants.All().
 func (ptcp *Participants) All() []*common.Player {
 	return ptcp.Called().Get(0).([]*common.Player)
diff --git a/game_state.go b/game_state.go
index 758c8d4..9f0a2fe 100644
--- a/game_state.go
+++ b/game_state.go
@@ -59,6 +59,7 @@ func (gs *GameState) Team(team common.Team) *common.TeamState {
 	} else if team == common.TeamCounterTerrorists {
 		return &gs.ctState
 	}
+
 	return nil
 }
 
@@ -179,6 +180,7 @@ func (ptcp Participants) ByUserID() map[int]*common.Player {
 			res[k] = v
 		}
 	}
+
 	return res
 }
 
@@ -190,6 +192,20 @@ func (ptcp Participants) ByEntityID() map[int]*common.Player {
 	for k, v := range ptcp.playersByEntityID {
 		res[k] = v
 	}
+
+	return res
+}
+
+// AllByUserID returns all currently known players & spectators, including disconnected ones,
+// in a map where the key is the user-ID.
+// The returned map is a snapshot and is not updated on changes (not a reference to the actual, underlying map).
+// Includes spectators.
+func (ptcp Participants) AllByUserID() map[int]*common.Player {
+	res := make(map[int]*common.Player)
+	for k, v := range ptcp.playersByUserID {
+		res[k] = v
+	}
+
 	return res
 }
 
@@ -200,6 +216,7 @@ func (ptcp Participants) All() []*common.Player {
 	for _, p := range ptcp.playersByUserID {
 		res = append(res, p)
 	}
+
 	return res
 }
 
@@ -210,6 +227,7 @@ func (ptcp Participants) Connected() []*common.Player {
 	for _, p := range original {
 		res = append(res, p)
 	}
+
 	return res
 }
 
@@ -222,6 +240,7 @@ func (ptcp Participants) Playing() []*common.Player {
 			res = append(res, p)
 		}
 	}
+
 	return res
 }
 
@@ -234,6 +253,7 @@ func (ptcp Participants) TeamMembers(team common.Team) []*common.Player {
 			res = append(res, p)
 		}
 	}
+
 	return res
 }
 
@@ -261,6 +281,7 @@ func (ptcp Participants) SpottersOf(spotted *common.Player) (spotters []*common.
 			spotters = append(spotters, other)
 		}
 	}
+
 	return
 }
 
@@ -271,5 +292,6 @@ func (ptcp Participants) SpottedBy(spotter *common.Player) (spotted []*common.Pl
 			spotted = append(spotted, other)
 		}
 	}
+
 	return
 }
diff --git a/participants_interface.go b/participants_interface.go
index a42a327..f0cac6f 100644
--- a/participants_interface.go
+++ b/participants_interface.go
@@ -20,6 +20,11 @@ type IParticipants interface {
 	// The returned map is a snapshot and is not updated on changes (not a reference to the actual, underlying map).
 	// Includes spectators.
 	ByEntityID() map[int]*common.Player
+	// AllByUserID returns all currently known players & spectators, including disconnected ones,
+	// in a map where the key is the user-ID.
+	// The returned map is a snapshot and is not updated on changes (not a reference to the actual, underlying map).
+	// Includes spectators.
+	AllByUserID() map[int]*common.Player
 	// All returns all currently known players & spectators, including disconnected ones, of the demo.
 	// The returned slice is a snapshot and is not updated on changes.
 	All() []*common.Player


Test patch:
diff --git a/datatables_test.go b/datatables_test.go
index 01a2821..2c478ba 100644
--- a/datatables_test.go
+++ b/datatables_test.go
@@ -116,12 +116,14 @@ func testPlayerSpotted(t *testing.T, propName string) {
 func newParser() *Parser {
 	p := NewParser(new(DevNullReader))
 	p.header = &common.DemoHeader{}
+
 	return p
 }
 
 func fakePlayerEntity(id int) *fakest.Entity {
 	entity := new(fakest.Entity)
 	configurePlayerEntityMock(id, entity)
+
 	return entity
 }
 
diff --git a/demoinfocs_test.go b/demoinfocs_test.go
index 9eae96f..fa99e69 100644
--- a/demoinfocs_test.go
+++ b/demoinfocs_test.go
@@ -361,6 +361,7 @@ func BenchmarkConcurrent(b *testing.B) {
 func openFile(tb testing.TB, file string) *os.File {
 	f, err := os.Open(file)
 	assert.NoError(tb, err, "failed to open file %q", file)
+
 	return f
 }
 
@@ -369,6 +370,7 @@ func assertGolden(tb testing.TB, assertions *assert.Assertions, testCase string,
 	if ver := runtime.Version(); strings.Compare(ver, goldenVerificationGoVersionMin) < 0 {
 		tb.Logf("old go version %q detected, skipping golden file verification", ver)
 		tb.Logf("need at least version %q to compare against golden file", goldenVerificationGoVersionMin)
+
 		return
 	}
 
diff --git a/game_state_test.go b/game_state_test.go
index 5bbf32b..de29667 100644
--- a/game_state_test.go
+++ b/game_state_test.go
@@ -41,6 +41,7 @@ func TestGameState_Participants(t *testing.T) {
 	ptcp := gs.Participants()
 	byEntity := ptcp.ByEntityID()
 	byUserID := ptcp.ByUserID()
+	allByUserID := ptcp.AllByUserID()
 
 	// Should update ptcp as well since it uses the same map
 	gs.playersByEntityID[0] = newPlayer()
@@ -48,10 +49,23 @@ func TestGameState_Participants(t *testing.T) {
 
 	assert.Equal(t, gs.playersByEntityID, ptcp.ByEntityID())
 	assert.Equal(t, gs.playersByUserID, ptcp.ByUserID())
+	assert.Equal(t, gs.playersByUserID, ptcp.AllByUserID())
 
 	// But should not update byEntity or byUserID since they're copies
 	assert.NotEqual(t, byEntity, ptcp.ByEntityID())
-	assert.NotEqual(t, byUserID, ptcp.ByUserID())
+	byUserID2 := ptcp.ByUserID()
+	assert.NotEqual(t, byUserID, byUserID2)
+	assert.Equal(t, gs.playersByUserID, ptcp.AllByUserID())
+
+	gs.playersByEntityID[1] = newDisconnectedPlayer()
+	gs.playersByUserID[1] = newDisconnectedPlayer()
+
+	assert.Equal(t, gs.playersByUserID, ptcp.AllByUserID())
+
+	assert.NotEqual(t, byEntity, ptcp.ByEntityID())
+	// Should be equal since ByUserID() do not return disconnected users
+	assert.Equal(t, byUserID2, ptcp.ByUserID())
+	assert.NotEqual(t, allByUserID, ptcp.ByUserID())
 }
 
 func TestGameState_ConVars(t *testing.T) {
@@ -264,5 +278,13 @@ func newPlayer() *common.Player {
 	pl := common.NewPlayer(nil)
 	pl.Entity = new(st.Entity)
 	pl.IsConnected = true
+
+	return pl
+}
+
+func newDisconnectedPlayer() *common.Player {
+	pl := common.NewPlayer(nil)
+	pl.Entity = new(st.Entity)
+
 	return pl
 }