Przeglądaj źródła

:whale: SnowKit

渔民小镇 1 rok temu
rodzic
commit
346426919f

+ 44 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/SnowKit.java

@@ -0,0 +1,44 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow;
+
+import com.iohao.mmo.common.snow.core.IdGenerator;
+import com.iohao.mmo.common.snow.core.IdGeneratorException;
+import com.iohao.mmo.common.snow.core.IdGeneratorOptions;
+import com.iohao.mmo.common.snow.core.impl.DefaultIdGenerator;
+import lombok.Setter;
+import lombok.experimental.UtilityClass;
+
+/**
+ * 雪花工具
+ *
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+@UtilityClass
+public class SnowKit {
+    IdGeneratorOptions idGeneratorOptions = new IdGeneratorOptions();
+
+    @Setter
+    IdGenerator idGenerator = new DefaultIdGenerator(idGeneratorOptions);
+
+    public long next() throws IdGeneratorException {
+        return idGenerator.next();
+    }
+}

+ 27 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/core/IdGenerator.java

@@ -0,0 +1,27 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow.core;
+
+/**
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+public interface IdGenerator {
+    long next() throws IdGeneratorException;
+}

+ 34 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/core/IdGeneratorException.java

@@ -0,0 +1,34 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow.core;
+
+/**
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+public class IdGeneratorException extends RuntimeException {
+
+    public IdGeneratorException(String message) {
+        super(message);
+    }
+
+    public IdGeneratorException(String msgFormat, Object... args) {
+        super(String.format(msgFormat, args));
+    }
+}

+ 115 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/core/IdGeneratorOptions.java

@@ -0,0 +1,115 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow.core;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.FieldDefaults;
+
+/**
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+@Getter
+@Setter
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class IdGeneratorOptions {
+
+    /**
+     * 雪花计算方法
+     * <pre>
+     *     (1-漂移算法|2-传统算法),默认1
+     * </pre>
+     */
+    public short method = 1;
+
+    /**
+     * 基础时间(ms单位)
+     * <pre>
+     *     不能超过当前系统时间,默认为2023-01-01 00:00:00
+     * </pre>
+     */
+    public long baseTime = 1672502400000L;
+
+    /**
+     * 机器码
+     * <pre>
+     *     该实例机器码,必须唯一,必须由外部设定,最大值 2^WorkerIdBitLength-1
+     * </pre>
+     */
+    public short workerId = 0;
+
+    /**
+     * 机器码位长
+     * <pre>
+     *     决定项目集群能使用id最大机器数, 默认值6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过22)
+     * </pre>
+     */
+    public byte workerIdBitLength = 1;
+
+    /**
+     * 数据中心id
+     * <pre>
+     *     该实例集群机器码,必须唯一,必须由外部设定,最大值 2^DataCenterIdBitLength-1
+     * </pre>
+     */
+    public short dataCenterId = 0;
+
+    /**
+     * 数据中心id位长
+     * <pre>
+     *     决定项目集群能使用id最大机器数, 默认值0(取值范围[0,6],也就是说最多支持集群配置 2 ^ 6 = 64 个数据中心)
+     * </pre>
+     */
+    public byte dataCenterIdBitLength = 0;
+
+    /**
+     * 序列数位长
+     * <pre>
+     *     决定一毫秒能生成的最大id数,如果超过会阻塞,默认值6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过22)
+     * </pre>
+     */
+    public byte seqBitLength = 6;
+
+    /**
+     * 最大序列数(含)
+     * <pre>
+     *     设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值0,表示最大序列数取最大值(2^SeqBitLength-1])
+     * </pre>
+     */
+    public short maxSeqNumber = 0;
+
+    /**
+     * 最小序列数(含)
+     * <pre>
+     *     默认值5,取值范围 [5, MaxSeqNumber],每毫秒的前5个序列数对应编号是0-4是保留位,
+     *     其中1-4是时间回拨相应预留位,0是手工新值预留位
+     * </pre>
+     */
+    public short minSeqNumber = 5;
+
+    /**
+     * 最大漂移次数(含)
+     * <pre>
+     *     默认2000,推荐范围500-10000(与计算能力有关)
+     * </pre>
+     */
+    public short topOverCostCount = 2000;
+}

+ 27 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/core/SnowWorker.java

@@ -0,0 +1,27 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow.core;
+
+/**
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+public interface SnowWorker {
+    long next() throws IdGeneratorException;
+}

+ 111 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/core/impl/DefaultIdGenerator.java

@@ -0,0 +1,111 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow.core.impl;
+
+
+import com.iohao.mmo.common.snow.core.IdGenerator;
+import com.iohao.mmo.common.snow.core.IdGeneratorException;
+import com.iohao.mmo.common.snow.core.IdGeneratorOptions;
+import com.iohao.mmo.common.snow.core.SnowWorker;
+
+/**
+ * 构建配置类,检查参数是否合法,根据参数构建算法生成器
+ *
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+public class DefaultIdGenerator implements IdGenerator {
+    private static SnowWorker snowWorker;
+
+    /**
+     * 构造函数,检查参数是否都合法
+     *
+     * @throws IdGeneratorException
+     */
+    public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorException {
+        // 1.BaseTime
+        if (options.baseTime < 315504000000L || options.baseTime > System.currentTimeMillis()) {
+            throw new IdGeneratorException("BaseTime error.");
+        }
+
+        // 2.WorkerIdBitLength
+        if (options.workerIdBitLength <= 0) {
+            throw new IdGeneratorException("WorkerIdBitLength error.(range:[1, 21])");
+        }
+        if (options.workerIdBitLength + options.seqBitLength + options.dataCenterIdBitLength > 22) {
+            throw new IdGeneratorException("error:WorkerIdBitLength + SeqBitLength + DataCenterIdBitLength <= 22");
+        }
+
+        // 3.WorkerId
+        int maxWorkerIdNumber = (1 << options.workerIdBitLength) - 1;
+        if (maxWorkerIdNumber == 0) {
+            maxWorkerIdNumber = 63;
+        }
+
+        if (options.workerId < 0 || options.workerId > maxWorkerIdNumber) {
+            throw new IdGeneratorException("WorkerId error. (range:[0, " + (maxWorkerIdNumber > 0 ? maxWorkerIdNumber : 63) + "]");
+        }
+
+        // 4. DataCenterId
+        int maxDataCenterId = (1 << options.dataCenterIdBitLength) - 1;
+        if (options.dataCenterId < 0 || options.dataCenterId > maxDataCenterId) {
+            throw new IdGeneratorException("DataCenterId error. (range:[0," + maxDataCenterId + "])");
+        }
+
+        // 5.SeqBitLength
+        if (options.seqBitLength < 2 || options.seqBitLength > 21) {
+            throw new IdGeneratorException("SeqBitLength error. (range:[2, 21])");
+        }
+
+        // 6.MaxSeqNumber
+        int maxSeqNumber = (1 << options.seqBitLength) - 1;
+        if (maxSeqNumber == 0) {
+            maxSeqNumber = 63;
+        }
+
+        if (options.maxSeqNumber < 0 || options.maxSeqNumber > maxSeqNumber) {
+            throw new IdGeneratorException("MaxSeqNumber error. (range:[1, " + maxSeqNumber + "]");
+        }
+
+        // 7.MinSeqNumber
+        if (options.minSeqNumber < 5 || options.minSeqNumber > maxSeqNumber) {
+            throw new IdGeneratorException("MinSeqNumber error. (range:[5, " + maxSeqNumber + "]");
+        }
+
+        // 判断是构建雪花漂移算法还是普通雪花算法
+        if (options.method == 2) {
+            snowWorker = new SnowWorkerM2(options);
+        } else {
+            snowWorker = new SnowWorkerM1(options);
+        }
+
+        if (options.method == 1) {
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public long next() {
+        return snowWorker.next();
+    }
+}

+ 351 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/core/impl/SnowWorkerM1.java

@@ -0,0 +1,351 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow.core.impl;
+
+
+import com.iohao.mmo.common.snow.core.IdGeneratorException;
+import com.iohao.mmo.common.snow.core.IdGeneratorOptions;
+import com.iohao.mmo.common.snow.core.SnowWorker;
+import lombok.AccessLevel;
+import lombok.experimental.FieldDefaults;
+
+/**
+ * 雪花漂移算法核心代码
+ *
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+@FieldDefaults(level = AccessLevel.PROTECTED)
+public class SnowWorkerM1 implements SnowWorker {
+
+    /**
+     * 基础时间
+     */
+    final long baseTime;
+
+    /**
+     * 机器码
+     */
+    final short workerId;
+
+    /**
+     * 机器码位长
+     */
+    final byte workerIdBitLength;
+
+    /**
+     * 数据中心id
+     */
+    final short dataCenterId;
+
+    /**
+     * 数据中心id位长
+     */
+    final byte dataCenterIdBitLength;
+
+    /**
+     * 自增序列数位长
+     */
+    final byte seqBitLength;
+
+    /**
+     * 最大序列数(含)
+     */
+    final int maxSeqNumber;
+
+    /**
+     * 最小序列数(含)
+     */
+    final short minSeqNumber;
+
+    /**
+     * 最大漂移次数(含)
+     */
+    final int topOverCostCount;
+
+    /**
+     * 锁对象
+     */
+    final static byte[] SYNC_LOCK = new byte[0];
+
+    /**
+     * 时间戳位移位
+     */
+    final byte timestampShift;
+
+    /**
+     * 数据中心id位移
+     */
+    final byte dataCenterShift;
+
+    /**
+     * 当前能使用的序列数id
+     */
+    short currentSeqNumber;
+    /**
+     * 最后一次生成id的时间戳差值
+     */
+    long lastTimeTick = 0;
+    /**
+     * 回拨时间戳差值
+     */
+    long turnBackTimeTick = 0;
+    /**
+     * 回拨序数位索引
+     */
+    byte turnBackIndex = 0;
+    /**
+     * 超出当前毫秒序号最大长度标识
+     */
+    boolean overCost = false;
+    int overCostCountInOneTerm = 0;
+    int genCountInOneTerm = 0;
+    /**
+     * 序列数索引,用一个+1,【0,4】预留
+     */
+    int termIndex = 0;
+
+    /**
+     * 构造参数准备
+     *
+     * @param options
+     */
+    public SnowWorkerM1(IdGeneratorOptions options) {
+        baseTime = options.baseTime != 0 ? options.baseTime : 1582136402000L;
+        workerIdBitLength = options.workerIdBitLength == 0 ? 6 : options.workerIdBitLength;
+        workerId = options.workerId;
+        seqBitLength = options.seqBitLength == 0 ? 6 : options.seqBitLength;
+
+        //校验DataCenterId,如果DataCenterIdBitLength为0,那么DataCenterId强制为0
+        if (options.dataCenterIdBitLength == 0) {
+            dataCenterId = 0;
+        } else {
+            dataCenterId = options.dataCenterId;
+        }
+        dataCenterIdBitLength = options.dataCenterIdBitLength;
+
+        maxSeqNumber = options.maxSeqNumber <= 0 ? (1 << seqBitLength) - 1 : options.maxSeqNumber;
+        minSeqNumber = options.minSeqNumber;
+        topOverCostCount = options.topOverCostCount == 0 ? 2000 : options.topOverCostCount;
+        //时间戳位移为 机器码位长 + 数据中心id位长 + 序数号位长
+        timestampShift = (byte) (workerIdBitLength + dataCenterIdBitLength + seqBitLength);
+        //数据中心id位移为 机器码位长 + 数据中心id位长 + 序数号位长
+        dataCenterShift = (byte) (dataCenterIdBitLength + seqBitLength);
+        currentSeqNumber = minSeqNumber;
+    }
+
+    private void endOverCostAction(long useTimeTick) {
+        if (termIndex > 10000) {
+            termIndex = 0;
+        }
+    }
+
+    /**
+     * 正常的获取下一个id,生成id核心代码
+     *
+     * @return 下一个id
+     * @throws IdGeneratorException
+     */
+    private long nextNormalId() throws IdGeneratorException {
+        long currentTimeTick = getCurrentTimeTick();
+
+        //如果出现时间回拨
+        if (currentTimeTick < lastTimeTick) {
+            if (turnBackTimeTick < 1) {
+                turnBackTimeTick = lastTimeTick - 1;
+                turnBackIndex++;
+
+                // 每毫秒序列数的前5位是预留位,0用于手工新值,1-4是时间回拨次序
+                // 支持4次回拨次序(避免回拨重叠导致ID重复),可无限次回拨(次序循环使用)。
+                if (turnBackIndex > 4) {
+                    turnBackIndex = 1;
+                }
+
+            }
+
+            return calcTurnBackId(turnBackTimeTick);
+        }
+
+        // 时间追平时,_TurnBackTimeTick清零
+        if (turnBackTimeTick > 0) {
+            turnBackTimeTick = 0;
+        }
+
+        if (currentTimeTick > lastTimeTick) {
+            lastTimeTick = currentTimeTick;
+            currentSeqNumber = minSeqNumber;
+
+            return calcId(lastTimeTick);
+        }
+
+        //当前序列数
+        if (currentSeqNumber > maxSeqNumber) {
+
+            termIndex++;
+            lastTimeTick++;
+            currentSeqNumber = minSeqNumber;
+            overCost = true;
+            overCostCountInOneTerm = 1;
+            genCountInOneTerm = 1;
+
+            return calcId(lastTimeTick);
+        }
+
+        return calcId(lastTimeTick);
+    }
+
+    /**
+     * 超出该毫秒内的支持的生成数,生成id
+     *
+     * @return long生产的id
+     */
+    private long nextOverCostId() {
+        long currentTimeTick = getCurrentTimeTick();
+
+        //如果出现时间回拨
+        if (currentTimeTick > lastTimeTick) {
+            endOverCostAction(currentTimeTick);
+
+            lastTimeTick = currentTimeTick;
+            currentSeqNumber = minSeqNumber;
+            overCost = false;
+            overCostCountInOneTerm = 0;
+            genCountInOneTerm = 0;
+
+            return calcId(lastTimeTick);
+        }
+
+
+        if (overCostCountInOneTerm >= topOverCostCount) {
+            endOverCostAction(currentTimeTick);
+
+            lastTimeTick = getNextTimeTick();
+            currentSeqNumber = minSeqNumber;
+            overCost = false;
+            overCostCountInOneTerm = 0;
+            genCountInOneTerm = 0;
+
+            return calcId(lastTimeTick);
+        }
+
+        if (currentSeqNumber > maxSeqNumber) {
+            lastTimeTick++;
+            currentSeqNumber = minSeqNumber;
+            overCost = true;
+            overCostCountInOneTerm++;
+            genCountInOneTerm++;
+
+            return calcId(lastTimeTick);
+        }
+
+        genCountInOneTerm++;
+        return calcId(lastTimeTick);
+    }
+
+    /**
+     * 正常情况下,采用左位移拼接结果id
+     *
+     * @param useTimeTick 时间戳差值
+     * @return 生成的id
+     */
+    private long calcId(long useTimeTick) {
+        long result = shiftStitchingResult(useTimeTick);
+        currentSeqNumber++;
+        return result;
+    }
+
+    /**
+     * 发生时间回拨的时候,采用左位移拼接结果id
+     *
+     * @param useTimeTick 时间戳差值
+     * @return 生成的id
+     */
+    private long calcTurnBackId(long useTimeTick) {
+        long result = shiftStitchingResult(useTimeTick);
+        turnBackTimeTick--;
+        return result;
+    }
+
+    /**
+     * 左位移拼接返回的id
+     *
+     * @param useTimeTick 时间差值
+     * @return 生成的id
+     */
+    protected long shiftStitchingResult(long useTimeTick) {
+        /*
+         * 采用BigInteger重构,但是并发量可能会低,需要测试
+         * return BigInteger.valueOf(useTimeTick)
+         * .shiftLeft(_TimestampShift).add(BigInteger.valueOf(DataCenterId))
+         * .shiftLeft(_DataCenterShift).add(BigInteger.valueOf(WorkerId))
+         * .shiftLeft(SeqBitLength).add(BigInteger.valueOf(_CurrentSeqNumber));
+         */
+
+        //时间差值,时间戳位移 = 数据中心id位长 + 机器码位长 + 序数位长
+        return ((useTimeTick << timestampShift) +
+                //数据中心id,数据中心id位移 = 机器码位长 + 序数位长
+                ((long) dataCenterId << dataCenterShift) +
+                //机器码数,机器码位移 = 序数位长
+                ((long) workerId << seqBitLength) +
+                (int) currentSeqNumber);
+    }
+
+    /**
+     * 获取当前时间 - 系统时间差值
+     *
+     * @return 时间差值
+     */
+    protected long getCurrentTimeTick() {
+        long millis = System.currentTimeMillis();
+        return millis - baseTime;
+    }
+
+    /**
+     * 获取下次时间差值
+     *
+     * @return 时间差值
+     */
+    protected long getNextTimeTick() {
+        long tempTimeTicker = getCurrentTimeTick();
+        while (tempTimeTicker <= lastTimeTick) {
+            try {
+                //发生回拨等待 10 毫秒
+                Thread.sleep(10);
+            } catch (InterruptedException e) {
+                throw new IdGeneratorException("Error when time callback waits three millisecond");
+            }
+            tempTimeTicker = getCurrentTimeTick();
+        }
+
+        return tempTimeTicker;
+    }
+
+    /**
+     * 真正执行的方法,判断是否超出当前生成序数的最大值,执行不同的方法
+     *
+     * @return 生成的id
+     */
+    @Override
+    public long next() {
+        synchronized (SYNC_LOCK) {
+            return overCost ? nextOverCostId() : nextNormalId();
+        }
+    }
+}
+

+ 65 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/core/impl/SnowWorkerM2.java

@@ -0,0 +1,65 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.iohao.mmo.common.snow.core.impl;
+
+
+import com.iohao.mmo.common.snow.core.IdGeneratorException;
+import com.iohao.mmo.common.snow.core.IdGeneratorOptions;
+
+/**
+ * 传统雪花漂移算法核心代码
+ *
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+public class SnowWorkerM2 extends SnowWorkerM1 {
+
+    //调用父类构造
+    public SnowWorkerM2(IdGeneratorOptions options) {
+        super(options);
+    }
+
+    @Override
+    public long next() {
+        synchronized (SYNC_LOCK) {
+            long currentTimeTick = getCurrentTimeTick();
+
+            //如果最后一次生成与当前时间相同
+            if (lastTimeTick == currentTimeTick) {
+                //如果当前使用到的序列号已经大于最大序列号,就是用预留的插
+                if (currentSeqNumber++ > maxSeqNumber) {
+                    currentSeqNumber = minSeqNumber;
+                    currentTimeTick = getNextTimeTick();
+                }
+            } else {
+                currentSeqNumber = minSeqNumber;
+            }
+
+            //如果发生了时间回拨
+            if (currentTimeTick < lastTimeTick) {
+                throw new IdGeneratorException("Time error for {0} milliseconds", lastTimeTick - currentTimeTick);
+            }
+
+            lastTimeTick = currentTimeTick;
+
+            //位移并返回
+            return shiftStitchingResult(currentTimeTick);
+        }
+    }
+}

+ 27 - 0
common/common-core/src/main/java/com/iohao/mmo/common/snow/package-info.java

@@ -0,0 +1,27 @@
+/*
+ * ioGame
+ * Copyright (C) 2021 - 2023  渔民小镇 (262610965@qq.com、luoyizhu@gmail.com) . All Rights Reserved.
+ * # iohao.com . 渔民小镇
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+/**
+ * 雪花相关
+ *
+ * 参考 https://gitee.com/lmlx66/yitter-idgenerator-spring-boot-starter
+ *
+ * @author 渔民小镇
+ * @date 2023-07-27
+ */
+package com.iohao.mmo.common.snow;