Skip to content

Commit

Permalink
imp: 改进预览功能,尤其是对随机值和抽牌的处理 (#981)
Browse files Browse the repository at this point in the history
  • Loading branch information
fy0 authored Aug 12, 2024
1 parent 2366233 commit 82fa239
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 46 deletions.
1 change: 1 addition & 0 deletions dice/builtin_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
22 changes: 13 additions & 9 deletions dice/dice.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package dice
import (
"errors"
"fmt"
"math"
"os"
"path/filepath"
"runtime/debug"
Expand Down Expand Up @@ -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() {
Expand Down
6 changes: 4 additions & 2 deletions dice/ext_deck.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down
8 changes: 6 additions & 2 deletions dice/im_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

ds "github.com/sealdice/dicescript"
rand2 "golang.org/x/exp/rand"

"sealdice-core/dice/model"
"sealdice-core/message"
Expand Down Expand Up @@ -494,6 +495,7 @@ type MsgContext struct {
splitKey string
vm *ds.Context
AttrsCurCache *AttributesItem
_v1Rand *rand2.PCGSource
}

// fillPrivilege 填写MsgContext中的权限字段, 并返回填写的权限等级
Expand Down Expand Up @@ -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)
Expand Down
31 changes: 16 additions & 15 deletions dice/rollvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"

"github.com/samber/lo"
rand2 "golang.org/x/exp/rand"
)

type Type uint8
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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))
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: 类型错误")
}
Expand Down Expand Up @@ -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

Expand All @@ -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
}
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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: 类型错误")
}
Expand Down Expand Up @@ -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
}

Expand Down
72 changes: 57 additions & 15 deletions dice/rollvm_migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"sort"
"strconv"
"strings"
"time"

"github.com/samber/lo"
ds "github.com/sealdice/dicescript"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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"
}

Expand All @@ -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 {
Expand Down
Loading

0 comments on commit 82fa239

Please sign in to comment.