Skip to content

Commit

Permalink
feat(cardinal): deterministic wrapper around math/rand so users can u…
Browse files Browse the repository at this point in the history
…se it. (Argus-Labs#782)
  • Loading branch information
pyrofolium authored Jul 2, 2024
1 parent 4649a69 commit aa0f043
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 0 deletions.
23 changes: 23 additions & 0 deletions cardinal/cardinal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,29 @@ func TestCanQueryInsideSystem(t *testing.T) {
assert.Equal(t, gotNumOfEntities, wantNumOfEntities)
}

func TestRandomNumberGenerator(t *testing.T) {
tf := cardinal.NewTestFixture(t, nil)
world := tf.World
testAmount := 50
numbers1 := make([]int64, 0, testAmount)
err := cardinal.RegisterSystems(world, func(context cardinal.WorldContext) error {
time.Sleep(5 * time.Millisecond)
numbers1 = append(numbers1, context.Rand().Int63())
return nil
})
assert.NilError(t, err)
tf.StartWorld()
for i := 0; i < testAmount; i++ {
tf.DoTick()
}
mapOfNums := make(map[int64]bool)
for _, num := range numbers1 {
_, ok := mapOfNums[num]
assert.Assert(t, ok == false)
mapOfNums[num] = true
}
}

func TestCanGetTimestampFromWorldContext(t *testing.T) {
var ts uint64
tf := cardinal.NewTestFixture(t, nil)
Expand Down
18 changes: 18 additions & 0 deletions cardinal/world_context.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cardinal

import (
"math/rand"
"reflect"

"github.com/rotisserie/eris"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"

Expand Down Expand Up @@ -34,6 +36,8 @@ type WorldContext interface {
EmitStringEvent(string) error
// Namespace returns the namespace of the world.
Namespace() string
// Rand returns a random number generator that is seeded specifically for a current tick.
Rand() *rand.Rand

// For internal use.

Expand All @@ -60,6 +64,7 @@ type worldContext struct {
txPool *txpool.TxPool
logger *zerolog.Logger
readOnly bool
rand *rand.Rand
}

func newWorldContextForTick(world *World, txPool *txpool.TxPool) WorldContext {
Expand All @@ -68,6 +73,8 @@ func newWorldContextForTick(world *World, txPool *txpool.TxPool) WorldContext {
txPool: txPool,
logger: &log.Logger,
readOnly: false,
//nolint:gosec // we require manual in the rng which crypto/rand doesn't have, but math/rand does.
rand: rand.New(rand.NewSource(int64(world.timestamp.Load()))),
}
}

Expand All @@ -77,6 +84,7 @@ func NewWorldContext(world *World) WorldContext {
txPool: nil,
logger: &log.Logger,
readOnly: false,
rand: nil,
}
}

Expand All @@ -86,6 +94,7 @@ func NewReadOnlyWorldContext(world *World) WorldContext {
txPool: nil,
logger: &log.Logger,
readOnly: true,
rand: nil,
}
}

Expand All @@ -102,6 +111,15 @@ func (ctx *worldContext) Logger() *zerolog.Logger {
return ctx.logger
}

func (ctx *worldContext) Rand() *rand.Rand {
if ctx.rand == nil {
// a panic is thrown here instead of returning an error to maintain method chaining.
// ex: wCtx.rand().Int63()
panic(eris.New("rand is only useable on a context generated by newWorldContextForTick"))
}
return ctx.rand
}

func (ctx *worldContext) getMessageByType(mType reflect.Type) (types.Message, bool) {
return ctx.world.GetMessageByType(mType)
}
Expand Down

0 comments on commit aa0f043

Please sign in to comment.