菜鸡第一次看算法题,这篇笔记还是不要看比较好
计算机幻觉从入门到入土
[NOTICE] 本文仅为一篇我个人学习笔记,不适合作为其他人的学习引导。
因为博主写这篇笔记就是这个状态 ↓
↑ 为此专门查了怎么用more标签隐藏文章内容
Java 注解预处理 Annotation Processing & 代码生成
关于 Java 的注解预处理的资料实在是过于稀少,连stackoverflow上都没多少人研究,以致于我这个萌新在尝试使用注解预处理来生成代码时踩了不少坑,正好博客也快长草了,遂决定留一篇文章,希望能够对后来者有所帮助。
本文章同时对一般 Java 项目和 Android 项目适用。
为何使用 Java 注解预处理
诚然,用反射处理注解来替代代码的复制粘贴可以让代码更加简洁、易懂(优雅),但是,反射实在是太慢了。
啥?反射不慢?来来来,一个 Activity 就用几十次反射,要不要和复制粘贴做一下对比?(手动阴险)
那反射这么慢,有没有什么办法?当然就是今天的主题了——代码生成: 让编译器来给你“复制粘贴”,既优雅,又高效(反正生成的代码你也不看)。
如何使用 Java 注解预处理
关于注解预处理的基本使用方法的资料还是很多的,这里就不细说了,概括一下就是:
- 建一个类,继承并实现
javax.annotation.processing.AbstractProcessor
- 建一个
META-INF.services.javax.annotation.processing.Processor
- 在这里写上你的预处理器的完整类名(带包名)
注意:对于 Android 项目,你需要单独建立一个 “Java 类” 项目,不可以直接在原 Android 项目中使用 注解预处理,否则你会发现没有 javax 这个包。
然后,在 Android 项目的build.gradle
中的dependencies
添加annotationProcessor project(':项目名')
处理我们的注解
假定我们要处理的注解名为 ViewAutoLoad
,定义为:
@Retention(RetentionPolicy.CLASS) //保留此注解到编译期
@Target(ElementType.FIELD) //此注解只适用于“字段”
public @interface ViewAutoLoad {
}
本文通过介绍对字段注解的处理来讲述如何实现注解预处理,对于方法,用法其实没啥区别。
然后,重写 process
方法:
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return true;
}
为啥要留个 return true
? true表示这个注解已经被我们处理过了,编译器不用再调用其他注解处理器了。
然后开始写我们的处理代码,这里就有两种处理注解的办法了:
办法1:一次性全局处理注解
这种方法 不能 知道这个字段(方法)到底是哪个类的,自然也不能获取除了你正在处理的字段(方法)所在类的其他信息,但是用起来方便一些。
获取 全局所有 具有此注解的字段,然后用 processAnnotation
方法逐一处理它们:
roundEnv.getElementsAnnotatedWith(ViewAutoLoad.class).forEach(this::processAnnotation);
这里先讲一些常用操作,假定我们现在在实现上文的 processAnnotation
方法,它的方法签名为:
private void processFormNotEmpty(Element annotatedElement)
获取字段的类型
如果你将来要生成代码或者将注解用作编译时检查,十有八九要用到这个字段的类型。
TypeMirror fieldType = annotatedElement.asType();
获取这个字段的注解,或者注解的值
ViewAutoLoad annotation = annotatedElement.getAnnotation(ViewAutoLoad.class);
现在,你可以直接使用你在注解接口定义的方法了,虽然作为示例的 ViewAutoLoad
没定义任何方法。
假装定义了 value()
: annotation.value()
获取这个字段(方法)的名字
我觉得这个肯定会用吧
Name fieldVarName = annotatedElement.getSimpleName();
//string: fieldVarName.toString();
获取这个方法的修饰符
annotatedElement.getModifiers()
返回一个集合,这个集合装着 javax.lang.model.element.Modifier
这个枚举
办法2:逐类处理注解
虽然麻烦了点,但是这个办法让我们可以知道我们在处理哪个类了。
我们回到 process
方法:
Set<? extends Element> rootElements = roundEnv.getRootElements();
这次我们直接拿到所有编译器处理的类的基础信息了,嗯,没有过滤器。
现在我们得手撸过滤器了,既然是 Set,先遍历走起。
然后怎么过滤呢?这里有一些思路:
- 给字段(方法)上注解的时候就指定好这个类的名称,比如
@Example("com.kenvix.test.TestClass")
注意:不要指定成TestClass.class
,在编译期无法这样读取类名,因为类尚未编译。 - 遍历所有类,通过字段(方法)的一些特征查找这个类
第一种思路
第一种可以是十分简单粗暴了。
String targetName = "com.kenvix.test.TestClass";
Element targetClass = null;
for (Element element : rootElements) {
if(element.toString().equals(targetName)) {
targetClass = element;
break;
}
}
//这里只拿到了类,注解处理方法暂时省略,见下文。
第二种思路
显然,第一种实在是不怎么优雅,第二种方法又有这些思路:
- 先通过包名,滤掉所有绝对不相关的东西,比如 Android 项目中,包名符合
android.*
就可以去掉了。或者只看包名以自己项目开头的。 - 字段(方法)的名称有一些我们可以利用的特征,例如都以类名开头
- 一个类中所有字段(方法)的数量和名称是唯一的。也就是说,不存在两个类同时有一样数量的同名字段。
- 找到有目标注解的类后,将这个类和目标注解直接加到 Map
Map<Element, List<Element>> tasks = new HashMap<>();
for (Element classElement : rootElements) {
if(classElement.toString().startsWith(Environment.TargetAppPackage)) {
List<? extends Element> enclosedElements = classElement.getEnclosedElements();
for(Element enclosedElement : enclosedElements) {
List<? extends AnnotationMirror> annotationMirrors = enclosedElement.getAnnotationMirrors();
for (AnnotationMirror annotationMirror : annotationMirrors) {
if(ViewAutoLoad.class.getName().equals(annotationMirror.getAnnotationType().toString())) { //好像没有其他办法在这里判断是否是目标注解了
if(!tasks.containsKey(classElement))
tasks.put(classElement, new LinkedList<>());
tasks.get(classElement).add(enclosedElement);
}
}
}
}
}
这样,这个 Map<> 中就包含了我们需要的类和这个类持有的字段了,接下来进行处理即可
嗯?效率低?这是编译期,加钱换CPU或用第一种,请(手动滑稽)
生成代码
这里需要用到 javapoet 这个依赖,编辑gradle配置,加入依赖:
implementation 'com.squareup:javapoet:1.8.0'
然后重写 init 方法:
protected Types typeUtil;
protected Elements elementUtil;
protected Filer filer;
protected Messager messager;
protected ProcessingEnvironment processingEnv;
@Override
public synchronized final void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.processingEnv = processingEnv;
typeUtil = processingEnv.getTypeUtils();
elementUtil = processingEnv.getElementUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
onPreprocessorInit();
messager.printMessage(Diagnostic.Kind.NOTE, "Preprocessor: " + this.getClass().getSimpleName() + " Initialized");
}
回到 process 方法,刚才我们已经拿到了要处理的注解,接下来开始处理这些注解:
JavaPoet 资料到处都是啊,要写还不容易?
我咋取一个不可能导入的包的类型?
这问题还是很常见的,比如我们没法在一个 Java 项目中用 Android 包的东西,但是却需要生成相关的代码.
例如,我们需要用到一个类 AppCompatActivity,它在 android.support.v7.app
这个包,则可以这样写:
ClassName appCompatClass = ClassName.get("android.support.v7.app", "AppCompatActivity");
我咋表示类型通配符、泛型限定?
接上,我们还想表示 ? extends AppCompatActivity
,可以这样写:
MethodSpec.Builder builder = code; //这里是你的方法builder
builder.addTypeVariable(TypeVariableName.get("T", appCompatClass)).addParameter(TypeVariableName.get("T"), "target")
保存我们的生成的代码,并在下一步编译生成的代码
回到 process 方法,加上:
if(roundEnv.processingOver()) {
//创建FormChecker这个类
TypeSpec formChecker = TypeSpec.classBuilder("FormChecker")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethods(methods)
.build();
//创建类文件
JavaFile javaFile = JavaFile.builder("com.kenvix.eg.generated", formChecker)
.addFileComment(getFileHeader())
.build();
try {
javaFile.writeTo(filer);
} catch (IOException ex) {
throw new IllegalStateException(ex.toString());
}
}
对同一个 javaFile
, javaFile.writeTo(filer)
只能调用一次,故需要判断是否为最后一轮注解预处理。
其他的可以看看这篇文章,虽然标题挺扯的(够你🐴)
其他小问题
我咋调试啊
显然这个时候按 IDE 的断点按钮是莫得了。
直接 System.out
或 Logger
也不太好,分分钟被一堆垃圾编译消息淹没。用着还麻烦。
好吧,其实有个简单粗暴的方法,抛个运行时异常嘛,这样就能直接停止编译然后让 IDE 显示我们想要的东西了。
throw new IllegalStateException("something");
IDEA 对 addModifiers(), javaFile.writeTo(filer) 报错
IDEA bug
别理他,编译就行了
想用 getClass() 反射处理?
别想了,类都没编译好呢你get个啥
Ubuntu 上通过以太网分享网络连接(NAT)
Ubuntu 自带网络分享功能,但该功能很不稳定,往往断开连接后再连就无法使用了。
现在我们使用 DNSMASQ+IPTables 手动配置NAT.
禁用 systemd-resolved
Ubuntu 提供的 systemd-resolved 抢占53端口,首先禁用它。
systemctl stop systemd-resolved
systemctl disable systemd-resolved
删除不当设置
如果你之前配置过网络分享或已经用有线连接过电脑了,则需要这一步
运行 nm-connection-editor
删除所有有关你要分享的网卡的设置
安装并配置DNSMASQ
安装
apt install dnsmasq
service dnsmasq stop
nano /etc/dnsmasq.conf
编辑 /etc/dnsmasq.conf
,加入下列内容:
dns-forward-max=15000
#eno1为你的要分享的网卡名
interface=eno1
dhcp-range=192.168.33.2,192.168.33.150,255.255.255.0,12h
配置域名解析
nano /etc/resolv.conf
填写你的DNS服务器,例如:
nameserver 223.5.5.5
nameserver 223.6.6.6
nameserver 114.114.114.114
启用内核IPV4转发
/etc/sysctl.conf
加入:
net.ipv4.ip_forward=1
运行:
sysctl -p
配置转发,为网卡分配初始IP
eno1为你的要分享的网卡名
enp2s0为有网的(被分享的)网卡名
ifconfig eno1 192.168.33.2
iptables -t nat -A POSTROUTING -o enp2s0 -j MASQUERADE
iptables -A FORWARD -i eno1 -o enp2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT
上述内容重启后无效,可加入 /etc/rc.local
以开机自动应用。
启动DNSMASQ
systemctl enable dnsmasq
systemctl start dnsmasq
systemctl status dnsmasq
完事!
Windows 选择指定的网卡来开承载网络型热点
Windows 10 自带的移动热点比较废,只允许带8个设备,并且在断网后自动关闭,不能满足需求。
而通过 netsh
创建的 Microsoft 承载网络虽好,但却不支持选择用于创建热点的网卡。
本文介绍的玄学方法可以让用户做到自己选择网卡开 Microsoft 承载网络 热点。
- 打开 网络和 Internet 设置
- 更改适配器选项
- 选择你希望使用的网卡,重命名
- 起个字母排序在你不想使用的网卡的名字之前的名字,例如
!AWLAN
- 开!热!点!
netsh wlan set hostednetwork mode=allow ssid=namehere key=passwordhere keyUsage=persistent
netsh wlan start hostednetwork
博主真的是随便试出来的(
修复升级 Windows10 版本后所有内置应用闪退+第三方应用参数错误的问题
昨天把Windows10升级到1809后所有应用都挂了,应用商店和内置应用闪退,常用的应用参数错误,连WSL都出问题了。容我先亲切问候一下微软 :)
修复内置应用闪退、应用商店打不开
这里有 几个没啥用的方法,反正对我来说真的没啥用。
修复办法:
- 打开
C:\Program Files
- 显示隐藏的文件,找到
WindowsApps
,点击上方菜单的 共享 – 高级共享 - 修改所有者为
Everyone
- 启用继承,保存
说白就是因为 ALL APPLICATION PACKAGES
没有权限。
另外还有人说是因为N卡驱动问题、LicenseManager服务被禁用等等,反正我不是这个问题。
修复第三方应用参数错误
没修好,我选择重装
配置用于 Gradle + SQLite 的 jOOQ 3.11 代码自动生成 (已过期)
敬告:这篇文章已经过期,不适用于最新版的 JOOQ,也不支持 Java11+ 和 Gradle 6.x,关于最新版的 JOOQ 使用说明,点击此处请阅读我的新文章
为什么要写这篇文章
关于如何配置用于 Gradle + SQLite 的 jOOQ 3.11 代码自动生成的文档可谓少之又少,网络上大部分文档,要么是使用 Maven,要么是针对于早期版本的 jOOQ(并不向后兼容),而 jOOQ 官方文档又很不全面,以致于许多像我这样的萌新在初次接触 jooq 时踩了不少坑,浪费了不少宝贵的时间 :)
为何一定要使用 jOOQ
复制一下 别人对此的介绍:
JOOQ,全称Java Object Oriented Querying,即面向Java对象查询。它是Data Geekery公司研发的DA方案(Data Access Layer),主要解决两个问题:
- Hibernate 的抽象使得我们离SQL太远,对SQL的掌控力度弱
- JDBC 过于嘈杂,需要干的事情太多
JOOQ希望干的就是在上述两者中寻找一个最佳的平衡。它依据数据库中的表生成DA相关的代码,开发者将生成的代码引入项目中即可使用。
配置办法
官方文档 只贴了个代码,可以说是十分”友善”了
编辑 build.gradle
// Configure the Java plugin and the dependencies
apply plugin: 'java'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
//在此处放置你的项目的原有依赖
//添加jooq依赖
compile group: 'org.jooq', name: 'jooq', version: '3.11.5'
//<!> 一定要添加所用数据库的依赖,否则会报错而且不告诉你原因
runtime group: 'org.xerial', name: 'sqlite-jdbc', version: '3.25.2'
}
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
//添加jooq依赖
classpath 'org.jooq:jooq-codegen:3.11.5'
//<!> 一定要添加所用数据库的依赖,否则会报错而且不告诉你原因
classpath group: 'org.xerial', name: 'sqlite-jdbc', version: '3.25.2'
}
}
// Use your favourite XML builder to construct the code generation configuration file
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
.configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.11.0.xsd') {
jdbc() {
url('jdbc:sqlite:src/main/resources/database.db') // src/main/resources/database.db为数据库路径
//user() //不需要用户名,省略
//password() //不需要密码,省略
}
generator() {
database() {
includes('.*') //包括的数据表
excludes() //排除的数据表
inputSchema() //默认数据库
}
target() {
packageName('com.kenvix.pixiv.generated.jooq') //计划用于存储生成结果的包名
directory('src/main/java') //将生成结果储存于src/main/java
}
}
}
// Run the code generator
// ----------------------
org.jooq.codegen.GenerationTool.generate(writer.toString())
然后运行 gradlew buildEnvironment
看看jooq有没有什么警告,同时,你会发现代码生成好了。
配置用于 Gradle6.x + MySQL 8 的 jOOQ 3.14 代码自动生成 (已更新)
为什么要写这篇文章
之前介绍了一下在旧版Gradle、SQLite上配置JOOQ,不过那篇文章实在是太旧了,在新版Gradle上已无法使用,也不兼容Java11。
关于如何配置用于 Gradle + M 的 jOOQ 3.11 代码自动生成的文档可谓少之又少,网络上大部分文档,要么是使用 Maven,要么是针对于早期版本的 jOOQ(并不向后兼容),而 jOOQ 官方文档又很不全面,以致于许多像我这样的萌新在初次接触 jooq 时踩了不少坑,浪费了不少宝贵的时间 :)
为何一定要使用 jOOQ
复制一下 别人对此的介绍:
JOOQ,全称Java Object Oriented Querying,即面向Java对象查询。它是Data Geekery公司研发的DA方案(Data Access Layer),主要解决两个问题:
- Hibernate 的抽象使得我们离SQL太远,对SQL的掌控力度弱
- JDBC 过于嘈杂,需要干的事情太多
JOOQ希望干的就是在上述两者中寻找一个最佳的平衡。它依据数据库中的表生成DA相关的代码,开发者将生成的代码引入项目中即可使用。
配置办法
官方文档 只贴了个代码,可以说是十分”友善”了
编辑 build.gradle
// Configure the Java plugin and the dependencies
apply plugin: 'java'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
//在此处放置你的项目的原有依赖
//添加jooq依赖
compile group: 'org.jooq', name: 'jooq', version: '3.11.5'
//<!> 一定要添加所用数据库的依赖,否则会报错而且不告诉你原因
runtime group: 'org.xerial', name: 'sqlite-jdbc', version: '3.25.2'
}
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
//添加jooq依赖
classpath 'org.jooq:jooq-codegen:3.11.5'
//<!> 一定要添加所用数据库的依赖,否则会报错而且不告诉你原因
classpath group: 'org.xerial', name: 'sqlite-jdbc', version: '3.25.2'
}
}
// Use your favourite XML builder to construct the code generation configuration file
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
.configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.11.0.xsd') {
jdbc() {
url('jdbc:sqlite:src/main/resources/database.db') // src/main/resources/database.db为数据库路径
//user() //不需要用户名,省略
//password() //不需要密码,省略
}
generator() {
database() {
includes('.*') //包括的数据表
excludes() //排除的数据表
inputSchema() //默认数据库
}
target() {
packageName('com.kenvix.pixiv.generated.jooq') //计划用于存储生成结果的包名
directory('src/main/java') //将生成结果储存于src/main/java
}
}
}
// Run the code generator
// ----------------------
org.jooq.codegen.GenerationTool.generate(writer.toString())
然后运行 gradlew buildEnvironment
看看jooq有没有什么警告,同时,你会发现代码生成好了。
修复 Windows 环境下的程序访问 WSL 中的 MySQL 提示 Access Denied 的问题
症状
Windows 10 版本 1803 + Ubuntu 18.04
位于 Windows 下的程序(例如Navicat)连接 MySQL 提示 Access Denied
同样位于 WSL 的程序可以正常访问 MySQL
原因
MySQL 把 Windows 下的程序的连接视为远程 MySQL 请求,若你使用的 MySQL 用户没有远程权限,则会出错。
WSL 特性?#(滑稽)
解决办法
WSL 输入 mysql -uroot
use mysql;
update user set host = '%' where user = 'root';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '' WITH GRANT OPTION;
FLUSH PRIVILEGES;
现在就可以使用 root 连接了(无密码,本地开发环境搞个毛的密码)
修复 WSL 下 PHP+FastCGI 卡死的问题
症状
Windows 10 版本 1803 + Ubuntu 18.04
以 Nginx 服务端为例,访问多数 PHP 文件,PHP 会直接卡死。
访问那些极其简单的 PHP 文件 (例如Hello world, phpinfo()) 虽然可以加载出来但浏览器显示网页仍未加载完全。
原因
你肯定用了 Unix Socket 方式连接 Nginx 和 PHP,然而,WSL 对 Unix Socket 的支持有 bug….
解决办法
在 nginx.conf
的 server
节点添加:
fastcgi_buffering off;
使用任意磁盘或路径保存 Windows 文件历史记录
不知为何,博主的 Windows 不允许使用本机的机械硬盘保存文件历史记录,这大概又是一个秘制bug吧。
无论是 Windows7 的备份还是 Windows10 的文件历史记录,它们都允许用户选择网络位置保存数据,于是我们就可以这样操作:
\\localhost\X$\Path
该路径表示本机 X:
盘的 Path
文件夹
例如,我要将文件历史记录保存到 D:\FileHistory
,只需在 “文件历史记录” – 选择驱动器 – 添加网络位置 中输入 \\localhost\D$\FileHistory
[1.12.2+Mod] MoeCraft :: 自由开放的科技向公益 Mod 服务器
特色&概述
MoeCraft 创立于 2016 年六月,是一个非盈利的、旨在为热爱 MC 的玩家创造最优环境的公益 Minecraft 服务器
高水平
每份入服申请均由多名老玩家及操作员审核,最大限度确保新玩家素质达到要求
自由
MoeCraft 并无严格的规则来限制玩家的行为,MoeCraft 信任每一位玩家的自我约束能力,即,MoeCraft 开放了几乎一切能开放的功能给全部玩家。
你喜欢采矿镭射那高效直接的采矿手段吗?那就扔掉手中的钻头吧!
你喜欢核弹爆炸产生的快感吗?那就尽情享用吧!
本服服规即为:不做你认为不应该做的
平等
MoeCraft 旨在淡化 OP 对游戏的影响,我们认为,OP 亦为玩家。
同时,我们坚信,影响玩家的决策应该由玩家决定。
如何加入 MoeCraft
- 请打开 https://accounts.moecraft.net/index.php?m=home&c=apply&a=applyinvitecode 提交邀请码申请
- 我们会在最长 72 小时内审核您的申请。申请通过后您将收到邀请码邮件。您也可以手动查询申请状态
- 申请通过后,请您点击用户中心(https://accounts.moecraft.net/)上的注册按钮,输入信息完成注册。
- 完成注册后,按照提示输入 MC 玩家名确保可以登录游戏。
- 点击用户中心主页的“下载客户端”按钮下载 MoeCraft 客户端(根据你的系统选择客户端平台)
- 打开更新器,完成更新。(注意: 未来若有更新从这步开始操作)
- 按照更新器指示完成更新。
- 点击 Launcher,添加账号,账号类型选择 Authlib-Injector
- 输入你的用户中心邮箱和密码。
- 游戏选择“ MoeCraft ”或“ 1.12.2-Forge”,双击进入游戏.
注意: 若提示“无效的用户名”,则说明你操作错误,请关闭游戏,重新按照上面第六步操作。
注意: 若上述操作过程出现异常问题,请联系我们(见下文)
Mod 清单
ActuallyAdditions-1.12.2-r135.jar
ae2stuff-0.7.0.4-mc1.12.2.jar
AppleCore-mc1.12.2-3.1.3.jar
appliedenergistics2-rv5-stable-11.jar
Aroma1997Core-1.12.2-2.0.0.0.b155.jar
bdlib-1.14.3.12-mc1.12.2.jar
BiblioCraft[v2.4.5][MC1.12.2].jar
BrandonsCore-1.12-2.4.2.157-universal.jar
Chisel-MC1.12.2-0.2.0.31.jar
CodeChickenLib-1.12.2-3.1.9.344-universal.jar
CoFHCore-1.12.2-4.5.2.19-universal.jar
CoFHWorld-1.12.2-1.2.0.5-universal.jar
CompactSolars-1.12.2-5.0.17.340-universal.jar
CookingForBlockheads_1.12.2-6.4.40.jar
CraftTweaker2-1.12-4.1.9.jar
CustomSkinLoader_Forge-14.8.jar
Draconic-Evolution-1.12-2.3.11.290-universal.jar
extrautils2-1.12-1.7.6.jar
foamfix-0.9.9.1-1.12.2-law.jar
forestry_1.12.2-5.8.0.305.jar
Forgelin-1.7.4.jar
Hwyla-1.8.26-B41_1.12.2.jar
industrialcraft-2-2.8.83-ex112.jar
industrialforegoing-1.12.2-1.10.1-176.jar
ironchest-1.12.2-7.0.40.824.jar
jei_1.12.2-4.10.0.198.jar
Mantle-1.12-1.3.2.24.jar
mcjtylib-1.12-3.0.2.jar
Mekanism-1.12.2-9.4.13.349.jar
MekanismGenerators-1.12.2-9.4.13.349.jar
MTLib-3.0.5.jar
NBTEdit-0.7.jar
NotEnoughItems-1.12.2-2.4.1.233-universal.jar
OpenBlocks-1.12.2-1.7.6.jar
OpenComputers-MC1.12.2-1.7.2.67.jar
OpenModsLib-1.12.2-0.11.5.jar
Pam's+HarvestCraft+1.12.2u.jar
plustic-6.5.2.0.jar
RedstoneFlux-1.12-2.0.2.3-universal.jar
rftools-1.12-7.54.jar
roost-1.12-1.2.0.jar
SpiceOfLife-mc1.12-1.3.12.jar
TConstruct-1.12.2-2.10.1.84.jar
tesla-core-lib-1.12.2-1.0.14.jar
ThermalCultivation-1.12.2-0.3.0.7-universal.jar
ThermalDynamics-1.12.2-2.5.1.14-universal.jar
ThermalExpansion-1.12.2-5.5.0.29-universal.jar
ThermalFoundation-1.12.2-2.5.0.19-universal.jar
tinker_io-1.12.2-release+2.6.1.jar
TinkerToolLeveling-1.12.2-1.0.5.jar
UniDict-1.12.2-2.5c.jar
WanionLib-1.12.2-1.5.jar
woot-1.12.2-1.4.1.jar
联系我们
您可以通过以下方式联系我们:
电子邮件: admin$moecraft.net
用户中心的留言板(内容公开)
群
现已支持自助加入 MoeCraft Telegram 群,通过身份验证后即可加入
地址: https://t.me/MoeCraftBot