Tag

Java

log4j+slf4j Ignoring binding 的解决方案

SLF4J: Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier.
SLF4J: Ignoring binding found at [jar:file:/F:/AppData/Gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j-impl/2.20.0/7ab4f082fd162f60afcaf2b8744a3d959feab3e8/log4j-slf4j-impl-2.20.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See https://www.slf4j.org/codes.html#ignoredBindings for an explanation.

引入依赖org.apache.logging.log4j:log4j-slf4j-impl:$_version_时替换为org.apache.logging.log4j:log4j-slf4j2-impl:$_version_

参考官方文档

使用zxing生成&识别二维码

GitHub

zxing/zxing

引入依赖

Gradle

implementation 'com.google.zxing:core:3.4.1'

Maven

<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.4.1</version>
</dependency>

示例代码

import com.google.zxing.*
import com.google.zxing.common.HybridBinarizer
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import java.awt.geom.AffineTransform
import java.awt.image.BufferedImage


object QrUtil {
    private val mfw = MultiFormatWriter()
    private val encodeHints = object : HashMap<EncodeHintType, Any>() {
        init {
            put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H)
            put(EncodeHintType.CHARACTER_SET, Charsets.UTF_8)
            put(EncodeHintType.MARGIN, 1)
        }
    }
    
    private val decodeHints = object : HashMap<DecodeHintType, Any>() {
        init {
            put(DecodeHintType.CHARACTER_SET, Charsets.UTF_8)
            //优化精度
            put(DecodeHintType.TRY_HARDER, true)
            //复杂模式,开启PURE_BARCODE模式
            put(DecodeHintType.PURE_BARCODE, true)
        }
    }

    /**
     * 生成二维码
     *
     * 若宽/高小于生成的二维码的最小宽度则会忽略此宽/高度, 否则会生成此宽/高度的二维码图片
     *
     * @param content 正文
     * @param width 目标图片宽度
     * @param height 目标图片高度
     * @param color1 二维码颜色
     * @param color2 二维码背景
     * @return 二维码图片
     */
    @Throws(WriterException::class)
    fun getQrImage(
        content: String,
        width: Int,
        height: Int,
        color1: Int,
        color2: Int,
    ): BufferedImage {
        val bitMatrix = mfw.encode(content, BarcodeFormat.QR_CODE, width, height, encodeHints)
        val w = bitMatrix.width
        val h = bitMatrix.height
        val image = BufferedImage(w, h, 1)
        for (x in 0 until w) for (y in 0 until h) image.setRGB(x, y, if (bitMatrix[x, y]) color1 else color2)
        return image
    }

    /**
     * 识别二维码
     *
     * @param image 要识别的图片
     * @return 识别的结果
     */
    @Throws(NotFoundException::class)
    fun decode(image: BufferedImage): String {
        val source = BufferedImageLuminanceSource(image)
        val bitmap = BinaryBitmap(HybridBinarizer(source))
        return MultiFormatReader().decode(bitmap, decodeHints).text
    }

    class BufferedImageLuminanceSource(
        image: BufferedImage,
        left: Int = 0,
        top: Int = 0,
        width: Int = image.width,
        height: Int = image.height,
    ) :
        LuminanceSource(width, height) {
        private val image: BufferedImage
        private val left: Int
        private val top: Int
        override fun getRow(y: Int, rowBytes: ByteArray): ByteArray {
            var row = rowBytes
            require(!(y < 0 || y >= height)) { "Requested row is outside the image: $y" }
            if (row.size < width) row = ByteArray(width)
            image.raster.getDataElements(left, top + y, width, 1, row)
            return row
        }

        override fun getMatrix(): ByteArray {
            val width = width
            val height = height
            val area = width * height
            val matrix = ByteArray(area)
            image.raster.getDataElements(left, top, width, height, matrix)
            return matrix
        }

        override fun isCropSupported() = true
        override fun crop(left: Int, top: Int, width: Int, height: Int) =
            BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height)

        override fun isRotateSupported() = true

        override fun rotateCounterClockwise(): LuminanceSource {
            val sourceWidth = image.width
            val sourceHeight = image.height
            val transform = AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth.toDouble())
            val rotatedImage = BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY)
            val g = rotatedImage.createGraphics()
            g.drawImage(image, transform, null)
            g.dispose()
            val width = width
            return BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), height, width)
        }

        init {
            val sourceWidth = image.width
            val sourceHeight = image.height
            require(!(left + width > sourceWidth || top + height > sourceHeight)) { "Crop rectangle does not fit within image data." }
            for (y in top until top + height) {
                for (x in left until left + width) {
                    if (image.getRGB(x, y) and -0x1000000 == 0) {
                        image.setRGB(x, y, -0x1) // = white
                    }
                }
            }
            this.image = BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY)
            this.image.graphics.drawImage(image, 0, 0, null)
            this.left = left
            this.top = top
        }
    }
}

Java中获取到的颜色是负数

这几天写图片处理相关的代码的时候遇到个问题,BufferedImage.getRGB()的返回值有时会是负数

百度一番之后找到了原因:

颜色的取值范围0x00000000-0xffffffff,会超过int类型的最大值

为了能让其正常取值,超过int最大值的会变成 他本身 - 1 - 0xffffffff

这里我写了一段转化的代码供参考

/**
 * 处理颜色补码(- -> +)
 * 将可能为负的颜色转成正数的Long
 *
 * @param color 可能为负数的颜色
 * @return 经过转换的颜色
 */
fun fromRgb(color: Int): Long {
    return (if (color < 0) 0xffffffff + color + 1 else color).toLong()
}

/**
 * 处理颜色补码(+ -> -)
 * 将长度超过Int的颜色转成可能为负的Int颜色
 *
 * @param color 正数颜色, Long
 * @return 经过转换的颜色
 */
fun toRgb(color: Long): Int {
    return (if (color > Int.MAX_VALUE) (color - 1 - 0xffffffff) else color).toInt()
}

Java下载

Minecraft1.17及以后的版本需要Java17

Minecraft1.16及以前的版本可以用Java8-15

推荐使用8/11这两个LTS的版本,LTS意为长期支持版本

高版本Java优点

包含更加合理高效的GC(垃圾回收)机制,运行效率更高

高版本Java缺点

一些插件或mod可能尚不支持高版本的Java

各种安装包的区别

一般Java分为Jre和Jdk,Jdk要略微大一些,但是从使用角度来看并没有什么区别

如果你的操作系统是64位的,下载时请选择64位(x86-6464bit

如果你知道如何配置Path,也可以下载zip或tgz格式的压缩包,并在解压后自行配置Path,否则请下载exe或msi格式(linux选择rpm等)的安装包,安装包会帮你配置Path

linux配置path

win配置path

链接

提供一些我知道的下载链接,你可以自行选择

DragonWell8:https://github.com/alibaba/dragonwell8/releases/latest (仅JDK8,阿里巴巴优化版本)

DragonWell11:https://github.com/alibaba/dragonwell11/releases/latest (仅JDK11,阿里巴巴优化版本)

AdoptOpenJDK:https://adoptium.net/releases.html (包含各个版本的Jdk和Jre)

AzulZuluJDK:https://www.azul.com/downloads/?architecture=x86-64-bit&package=jdk (包含各个版本的Jdk和Jre)

OracleJDK:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html (仅Jdk)

OracleJRE:https://www.oracle.com/java/technologies/javase-downloads.html (仅Jre)

Minecraft服务端搭建与运行

安装Java

开服需要安装Java (64位机器装64位Java)

安装Java教程

获取核心

首先选择自己要使用的核心和游戏版本

新建一个文件夹,确保其路径没有中文

下载核心后移动到此文件夹

此处注意,forge和fabric的核心是需要通过安装获得的,其他核心大多是一个单独的Jar文件,会自动下载需要的lib文件

移动核心时

  • forge请连着 libraries 文件夹一起
  • fabric请连着 .fabric.fabric-installer 两个文件夹一起

启动

官服的jar文件可以双击打开,但是其他的核心据我所知均需要使用开服脚本,开服脚本教程

现在的文件夹中应该有核心和启动脚本两个文件

win双击start.bat启动服务器,linux在命令行中输入./start.sh (无法执行的看脚本教程中的添加权限)

启动之后如果是没有自动重启的脚本会一闪然后关闭,此时文件夹中会自动生成 eula.txt

打开 eula.txt ,将其中的 false 改成 true (注意不要打错单词),保存并关闭

此时再启动脚本,服务端会正常启动