From 82fa239ad592515e24ccd523042b389c20b6e9b0 Mon Sep 17 00:00:00 2001 From: fy Date: Mon, 12 Aug 2024 13:41:37 +0800 Subject: [PATCH] =?UTF-8?q?imp:=20=E6=94=B9=E8=BF=9B=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=B0=A4=E5=85=B6=E6=98=AF=E5=AF=B9?= =?UTF-8?q?=E9=9A=8F=E6=9C=BA=E5=80=BC=E5=92=8C=E6=8A=BD=E7=89=8C=E7=9A=84?= =?UTF-8?q?=E5=A4=84=E7=90=86=20(#981)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dice/builtin_commands.go | 1 + dice/dice.go | 22 +++++++----- dice/ext_deck.go | 6 ++-- dice/im_session.go | 8 +++-- dice/rollvm.go | 31 ++++++++--------- dice/rollvm_migrate.go | 72 +++++++++++++++++++++++++++++++--------- go.mod | 2 +- go.sum | 4 +-- 8 files changed, 100 insertions(+), 46 deletions(-) diff --git a/dice/builtin_commands.go b/dice/builtin_commands.go index 4814ad10..17ef04b6 100644 --- a/dice/builtin_commands.go +++ b/dice/builtin_commands.go @@ -1210,6 +1210,7 @@ func (d *Dice) registerCoreCommands() { r, detail, err = DiceExprEvalBase(ctx, cmdArgs.CleanArgs, RollExtraFlags{ DefaultDiceSideNum: getDefaultDicePoints(ctx), DisableBlock: true, + V2Only: true, }) if r != nil && !r.IsCalculated() { diff --git a/dice/dice.go b/dice/dice.go index 1970f6c0..94f04bda 100644 --- a/dice/dice.go +++ b/dice/dice.go @@ -3,7 +3,6 @@ package dice import ( "errors" "fmt" - "math" "os" "path/filepath" "runtime/debug" @@ -762,22 +761,27 @@ func (d *Dice) GameSystemTemplateAdd(tmpl *GameSystemTemplate) bool { return false } -var randSource = rand2.NewSource(uint64(time.Now().Unix())) +// var randSource = rand2.NewSource(uint64(time.Now().Unix())) +var randSource = &rand2.PCGSource{} func DiceRoll(dicePoints int) int { //nolint:revive if dicePoints <= 0 { return 0 } - val := int(randSource.Uint64()%math.MaxInt32)%dicePoints + 1 - return val + val := ds.Roll(randSource, ds.IntType(dicePoints), 0) + return int(val) } -func DiceRoll64(dicePoints int64) int64 { //nolint:revive - if dicePoints == 0 { - return 0 +func DiceRoll64x(src *rand2.PCGSource, dicePoints int64) int64 { //nolint:revive + if src == nil { + src = randSource } - val := int64(randSource.Uint64()%math.MaxInt64)%dicePoints + 1 - return val + val := ds.Roll(src, ds.IntType(dicePoints), 0) + return int64(val) +} + +func DiceRoll64(dicePoints int64) int64 { //nolint:revive + return DiceRoll64x(nil, dicePoints) } func CrashLog() { diff --git a/dice/ext_deck.go b/dice/ext_deck.go index 8da0764e..37c20ffa 100644 --- a/dice/ext_deck.go +++ b/dice/ext_deck.go @@ -994,7 +994,7 @@ func executeDeck(ctx *MsgContext, deckInfo *DeckInfo, deckName string, shufflePo if pool == nil { return "", errors.New("牌组为空,可能尚未加载完成") } - key = pool.Pick().(string) + key = pool.PickSource(randSourceDrawAndTmplSelect).(string) } cmd, err := deckStringFormat(ctx, deckInfo, key) return cmd, err @@ -1088,11 +1088,13 @@ func NewChooser(choices ...wr.Choice) (*ShuffleRandomPool, error) { return &ShuffleRandomPool{data: choices, totals: totals, max: runningTotal}, nil } +var randSourceDrawAndTmplSelect = rand.New(rand.NewSource(time.Now().UnixMilli())) + // Pick returns a single weighted random Choice.Item from the Chooser. // // Utilizes global rand as the source of randomness. func (c *ShuffleRandomPool) Pick() interface{} { - r := rand.Intn(c.max) + 1 + r := randSourceDrawAndTmplSelect.Intn(c.max) + 1 i := searchInts(c.totals, r) theOne := c.data[i] diff --git a/dice/im_session.go b/dice/im_session.go index f6cedc0b..740f38e0 100644 --- a/dice/im_session.go +++ b/dice/im_session.go @@ -12,6 +12,7 @@ import ( "time" ds "github.com/sealdice/dicescript" + rand2 "golang.org/x/exp/rand" "sealdice-core/dice/model" "sealdice-core/message" @@ -494,6 +495,7 @@ type MsgContext struct { splitKey string vm *ds.Context AttrsCurCache *AttributesItem + _v1Rand *rand2.PCGSource } // fillPrivilege 填写MsgContext中的权限字段, 并返回填写的权限等级 @@ -2059,14 +2061,16 @@ func (ctx *MsgContext) Notice(txt string) { go foo() } +var randSourceSplitKey = rand2.NewSource(uint64(time.Now().Unix())) + func (ctx *MsgContext) InitSplitKey() { if len(ctx.splitKey) > 0 { return } - r := randSource.Uint64() + r := randSourceSplitKey.Uint64() bArray := make([]byte, 12) binary.LittleEndian.PutUint64(bArray[:8], r) - r = randSource.Uint64() + r = randSourceSplitKey.Uint64() binary.LittleEndian.PutUint32(bArray[8:], uint32(r)) s := base64.StdEncoding.EncodeToString(bArray) diff --git a/dice/rollvm.go b/dice/rollvm.go index 9e00964a..5002b938 100644 --- a/dice/rollvm.go +++ b/dice/rollvm.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/samber/lo" + rand2 "golang.org/x/exp/rand" ) type Type uint8 @@ -655,7 +656,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e continue case TypeDiceWod: t := &stack[top-1] // 加骰线 - ret, nums, rounds, details := DiceWodRollVM(e, t, wodState.pool, wodState.points, wodState.threshold, wodState.isGE) + ret, nums, rounds, details := DiceWodRollVM(ctx._v1Rand, e, t, wodState.pool, wodState.points, wodState.threshold, wodState.isGE) if e.Error != nil { return nil, "", e.Error } @@ -690,7 +691,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e continue case TypeDiceDC: t := &stack[top-1] // 暴击值 / 也可以理解为加骰线 - ret, nums, rounds, details := DiceDCRollVM(e, t, dcState.pool, dcState.points) + ret, nums, rounds, details := DiceDCRollVM(ctx._v1Rand, e, t, dcState.pool, dcState.points) if e.Error != nil { return nil, "", e.Error } @@ -718,7 +719,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e continue case TypeDicePenalty, TypeDiceBonus: t := stack[top-1] - diceResult := DiceRoll64(100) + diceResult := DiceRoll64x(ctx._v1Rand, 100) diceTens := diceResult / 10 diceUnits := diceResult % 10 @@ -732,7 +733,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e } for i := int64(0); i < t.Value.(int64); i++ { - n := DiceRoll64(10) + n := DiceRoll64x(ctx._v1Rand, 10) if n == 10 { num10Exists = true @@ -1041,7 +1042,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e case TypeDiceUnary: a := &stack[top-1] // Dice XXX, 如 d100 - a.Value = DiceRoll64(a.Value.(int64)) + a.Value = DiceRoll64x(ctx._v1Rand, a.Value.(int64)) e.Calculated = true continue case TypeHalt: @@ -1315,7 +1316,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e if e.flags.BigFailDiceOn { nums = append(nums, bInt) } else { - nums = append(nums, DiceRoll64(bInt)) + nums = append(nums, DiceRoll64x(ctx._v1Rand, bInt)) } } @@ -1358,7 +1359,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e if e.flags.BigFailDiceOn { curNum = bInt } else { - curNum = DiceRoll64(bInt) + curNum = DiceRoll64x(ctx._v1Rand, bInt) } num += curNum @@ -1386,7 +1387,7 @@ func (e *RollExpression) Evaluate(_ *Dice, ctx *MsgContext) (*VMStack, string, e return &stack[0], calcDetail, nil } -func DiceDCRollVM(e *RollExpression, addLine *VMValue, pool *VMValue, points *VMValue) (*VMValue, int64, int64, []string) { //nolint:revive +func DiceDCRollVM(randSrc *rand2.PCGSource, e *RollExpression, addLine *VMValue, pool *VMValue, points *VMValue) (*VMValue, int64, int64, []string) { //nolint:revive makeE6 := func() { e.Error = errors.New("E6: 类型错误") } @@ -1420,11 +1421,11 @@ func DiceDCRollVM(e *RollExpression, addLine *VMValue, pool *VMValue, points *VM return nil, 0, 0, nil } - ret1, ret2, ret3, details := DiceDCRoll(valAddLine, valPool, valPoints) + ret1, ret2, ret3, details := DiceDCRoll(randSrc, valAddLine, valPool, valPoints) return &VMValue{TypeID: VMTypeInt64, Value: ret1}, ret2, ret3, details } -func DiceDCRoll(addLine int64, pool int64, points int64) (int64, int64, int64, []string) { //nolint:revive +func DiceDCRoll(randSrc *rand2.PCGSource, addLine int64, pool int64, points int64) (int64, int64, int64, []string) { //nolint:revive var details []string addTimes := 1 @@ -1438,7 +1439,7 @@ func DiceDCRoll(addLine int64, pool int64, points int64) (int64, int64, int64, [ maxDice := int64(0) for i := int64(0); i < pool; i++ { - one := DiceRoll64(points) + one := DiceRoll64x(randSrc, points) if one > maxDice { maxDice = one } @@ -1482,7 +1483,7 @@ func DiceDCRoll(addLine int64, pool int64, points int64) (int64, int64, int64, [ return resultDice, allRollCount, int64(addTimes), details } -func DiceWodRoll(addLine int64, pool int64, points int64, threshold int64, isGE bool) (int64, int64, int64, []string) { //nolint:revive +func DiceWodRoll(randSrc *rand2.PCGSource, addLine int64, pool int64, points int64, threshold int64, isGE bool) (int64, int64, int64, []string) { //nolint:revive var details []string addTimes := 1 @@ -1497,7 +1498,7 @@ func DiceWodRoll(addLine int64, pool int64, points int64, threshold int64, isGE for i := int64(0); i < pool; i++ { var reachSuccess bool var reachAddRound bool - one := DiceRoll64(points) + one := DiceRoll64x(randSrc, points) if addLine != 0 { reachAddRound = one >= addLine @@ -1550,7 +1551,7 @@ func DiceWodRoll(addLine int64, pool int64, points int64, threshold int64, isGE return successCount, allRollCount, int64(addTimes), details } -func DiceWodRollVM(e *RollExpression, addLine *VMStack, pool *VMValue, points *VMValue, threshold *VMValue, isGE bool) (*VMValue, int64, int64, []string) { //nolint:revive +func DiceWodRollVM(randSrc *rand2.PCGSource, e *RollExpression, addLine *VMStack, pool *VMValue, points *VMValue, threshold *VMValue, isGE bool) (*VMValue, int64, int64, []string) { //nolint:revive makeE6 := func() { e.Error = errors.New("E6: 类型错误") } @@ -1592,7 +1593,7 @@ func DiceWodRollVM(e *RollExpression, addLine *VMStack, pool *VMValue, points *V return nil, 0, 0, nil } - ret1, ret2, ret3, details := DiceWodRoll(valAddLine, valPool, valPoints, valThreshold, isGE) + ret1, ret2, ret3, details := DiceWodRoll(randSrc, valAddLine, valPool, valPoints, valThreshold, isGE) return &VMValue{TypeID: VMTypeInt64, Value: ret1}, ret2, ret3, details } diff --git a/dice/rollvm_migrate.go b/dice/rollvm_migrate.go index 487eea35..39cfc908 100644 --- a/dice/rollvm_migrate.go +++ b/dice/rollvm_migrate.go @@ -8,6 +8,7 @@ import ( "sort" "strconv" "strings" + "time" "github.com/samber/lo" ds "github.com/sealdice/dicescript" @@ -92,7 +93,7 @@ func DiceFormatTmpl(ctx *MsgContext, s string) string { if a == nil { text = "<%未知项-" + s + "%>" } else { - text = ctx.Dice.TextMap[s].Pick().(string) + text = ctx.Dice.TextMap[s].PickSource(randSourceDrawAndTmplSelect).(string) // 找出其兼容情况,以决定使用什么版本的引擎 engineVersion := "v2" @@ -422,7 +423,6 @@ func (ctx *MsgContext) CreateVmIfNotExists() { ctx.vm.Config.EnableDiceCoC = true ctx.vm.Config.EnableDiceFate = true ctx.vm.Config.EnableDiceDoubleCross = true - ctx.vm.Config.EnableV1IfCompatible = true ctx.vm.Config.OpCountLimit = 30000 am := ctx.Dice.AttrsManager @@ -512,7 +512,7 @@ func (ctx *MsgContext) CreateVmIfNotExists() { if v == nil && strings.Contains(name, ":") { textTmpl := ctx.Dice.TextMap[name] if textTmpl != nil { - if v2, err := DiceFormatV2(ctx, textTmpl.Pick().(string)); err == nil { + if v2, err := DiceFormatV2(ctx, textTmpl.PickSource(randSourceDrawAndTmplSelect).(string)); err == nil { return ds.NewStrVal(v2) } } else { @@ -710,15 +710,15 @@ func _MsgCreate(messageType string, message string) *Message { messageType = "private" } - userID := "UI:1001" + userID := "UI:1101" groupID := "" groupName := "" groupRole := "" if messageType == "group" { - userID = "UI:1002" + userID = "UI:1101" messageType = "group" - groupID = "UI-Group:2001" - groupName = "UI-Group 2001" + groupID = "UI-Group:2101" + groupName = "UI-Group 2101" groupRole = "owner" } @@ -743,40 +743,82 @@ func TextMapCompatibleCheck(d *Dice, category, k string, textItems []TextTemplat key := fmt.Sprintf("%s:%s", category, k) x, _ := d.TextMapCompatible.LoadOrStore(key, &SyncMap[string, TextItemCompatibleInfo]{}) + am := d.AttrsManager + for _, textItem := range textItems { formatExpr := textItem[0].(string) msg := _MsgCreate("group", "") - // 注: 由于选择了真正执行一遍的方式,可能会有部分影响溢出导致修改到测试用户的数据 - // 但是这个测试用户是 UI:1001 所以姑且认为没有问题 + tmpSeed := []byte("1234567890ABCDEF") + tmpSeed2 := uint64(time.Now().UnixMicro()) + randSourceDrawAndTmplSelect.Seed(int64(tmpSeed2)) + + setupTestAttrs := func(ctx *MsgContext) { + // $g + if attrs, _ := am.LoadById("UI-Group:2101"); attrs != nil { + attrs.Clear() + attrs.IsSaved = true + } + // $m + if attrs, _ := am.LoadById("UI:1101"); attrs != nil { + attrs.Clear() + attrs.IsSaved = true + } + // 群内临时人物卡 + if attrs, _ := am.LoadById("UI-Group:2101-UI:1101"); attrs != nil { + attrs.Clear() + attrs.IsSaved = true + } + + // $t + ctx.Player.ValueMapTemp = &ds.ValueMap{} + } + + // v2 部分 ctx := CreateTempCtx(d.UIEndpoint, msg) + setupTestAttrs(ctx) ctx.CreateVmIfNotExists() + ctx.vm.Seed = tmpSeed + ctx.vm.Init() + ctx.splitKey = "###SPLIT-KEY###" + if a, exists := _textMapTestData2[key]; exists { if x, err := a.ToJSON(); err == nil { _ = json.Unmarshal(x, ctx.vm.Attrs) // TODO: 性能好一点的clone } - for k, v := range _textMapBuiltin { - ctx.vm.Attrs.Store(k, v.Clone()) - } } + for k, v := range _textMapBuiltin { + ctx.vm.Attrs.Store(k, v.Clone()) + } + text2, err2 := DiceFormatV2(ctx, formatExpr) + // v1 部分 ctx = CreateTempCtx(d.UIEndpoint, msg) + setupTestAttrs(ctx) + ctx.CreateVmIfNotExists() // 也要设置,因为牌堆要用 + ctx.vm.Seed = tmpSeed + ctx.vm.Init() + ctx.splitKey = "###SPLIT-KEY###" + ctx._v1Rand = ctx.vm.RandSrc + randSourceDrawAndTmplSelect.Seed(int64(tmpSeed2)) + _, presetExists := _textMapTestData2[key] if a, exists := _textMapTestData2[key]; exists { if x, err := a.ToJSON(); err == nil { _ = json.Unmarshal(x, ctx.vm.Attrs) // TODO: 性能好一点的clone } - for k, v := range _textMapBuiltin { - ctx.vm.Attrs.Store(k, v.Clone()) - } + } + for k, v := range _textMapBuiltin { + ctx.vm.Attrs.Store(k, v.Clone()) } text1, err1 := DiceFormatV1(ctx, formatExpr) if err1 != nil { text1 = "" // 因为 formatV1 没有值的时候会返回东西,这样使得两版本一致 } + setupTestAttrs(ctx) // 清理 var ver string if err2 == nil { diff --git a/go.mod b/go.mod index a5679231..217a488f 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/samber/lo v1.44.0 github.com/schollz/progressbar/v3 v3.14.4 github.com/sealdice/botgo v0.0.0-20240102160217-e61d5bdfe083 - github.com/sealdice/dicescript v0.0.0-20240804154241-fe03d285e730 + github.com/sealdice/dicescript v0.0.0-20240808080124-e177356bbeb5 github.com/slack-go/slack v0.13.0 github.com/sunshineplan/imgconv v1.1.4 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a diff --git a/go.sum b/go.sum index ca74b094..3a5ed2fd 100644 --- a/go.sum +++ b/go.sum @@ -337,8 +337,8 @@ github.com/schollz/progressbar/v3 v3.14.4 h1:W9ZrDSJk7eqmQhd3uxFNNcTr0QL+xuGNI9d github.com/schollz/progressbar/v3 v3.14.4/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI= github.com/sealdice/botgo v0.0.0-20240102160217-e61d5bdfe083 h1:s/jzCGYlM/0+TYTXwva5574EFnIv/ggPCoXHFpdbSUw= github.com/sealdice/botgo v0.0.0-20240102160217-e61d5bdfe083/go.mod h1:MGtR0REQDslBwQE+Rln4P9iDjH/ZInlu5qzOLdvBWJU= -github.com/sealdice/dicescript v0.0.0-20240804154241-fe03d285e730 h1:LxYs+eIpvZ7Tje6i6VkBPXxWtIkFbVV+RzWALatcyFo= -github.com/sealdice/dicescript v0.0.0-20240804154241-fe03d285e730/go.mod h1:uof752qJvEQ4Kze+NVag+RKGgj5C4K3kMHoK3e2vOLg= +github.com/sealdice/dicescript v0.0.0-20240808080124-e177356bbeb5 h1:8Q4evtC6pCVMhMivzbeC0/xebOVHcAzzVTVXMJCJhKY= +github.com/sealdice/dicescript v0.0.0-20240808080124-e177356bbeb5/go.mod h1:uof752qJvEQ4Kze+NVag+RKGgj5C4K3kMHoK3e2vOLg= github.com/sealdice/kook v0.0.3 h1:STMtiKRMFjhSFmUxi0BU5ktNkCQ8qi7Y5EEfrmYKvWY= github.com/sealdice/kook v0.0.3/go.mod h1:WjHC7AmbmNjInT/U/etBVOmAw7T6EqdCwApceRGs1sk= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=