2018年8月10日星期五

Chatper 2 - 指令与循环 Command and Loops


请看了上一篇再来哟



指令与循环 Command & Loops

在Skript 中 command 设定的方式很简单, 可是他拥有另一个Command 叫 Custom Command. 开发者能通过 Custom command 直接在指令上宣告 Expression 的参数 ( Arguments ).

另外提醒 [] 是 Optional 的意思 而 <> 是 required的意思哟.

// 普通 Command 开发的例子

command /give [<text>]:
    description: give command // 指令介绍
    usage: /give <item_ids> // 指令使用方式
    aliases: g              // 指令的 其他缩称 
    executable by: players // 谁 可以执行这个指令 ( console, players )
    trigger:
        // logic code

上面的是平常 command的开发例子 经常看到的 在logic code 上需要做一些侦测 但是使用 custom command的话就不需要那些侦测 甚至是 list类型的 expression.

// Custom Command 开发的例子

command /give <items>:
    description: give command // 指令介绍
    usage: /give <item_ids> // 指令使用方式
    aliases: g              // 指令的 其他缩称 
    executable by: players // 谁 可以执行这个指令 ( console, players )
    trigger:
        // logic code

如你所见 custom command 的 <items> 其实就是 expression %item% 只是设定成list 就多一个 's' 如果你只是要一个物品放 <item> 也能的. 列表类型的 expression 你要获得必须使用 loop.



什么是loop?Loop 可以说是 循环 一直重复做一个事情. Loop 可以循环读取 列表类型的变数. 在上面的command 例子 其实 command 里面的都叫 arguments 所有你能用以下的方式获得 items

// Loop 的例子

loop arguments:  // 循环读取 command 获得的 arguments
    就会从 第一个 arguments 读取到 最后一个才 停止这边的 运行

读取列表变数 里面的东西 要怎么获得?loop-index? loop-value? loop-item? 其实他们都有指定的用途和来源

// Loop 的例子

{_list::*} // 假如列表里面有 玩家A, 玩家B, 玩家C

loop {_list::*}:    // 我们需要这样去循环读取
    loop-index      // 循环开始的第一次 index 是 1
    loop-player     // player 是 玩家A
    loop-value      // 也是 玩家A
    loop-item       // 会有问题 (error) , 因为列表里面不是 %item%

为什么这样?那么多东西弄乱我们?并不是只是用些人的习惯 要知道 loop着什么东西其实 loop里面 一定会有的是 index 和 value. 分别为 目前循环到第几个 (index) 与 什么东西(value). 额为什么会有 loop-player, loop-item 这种? 你就要看看 expression了 记得expression的东西吗?%item(s)% , %player(s)% 这些东西都是有自己的名称,你在loop指定的 东西下 你可以直接写 loop-expression的名字下去。给个例子 我创建了一个 expression叫做 %haha(s)%. 如果我loop的 列表里面的 东西是 %haha% 我也可以通过 loop-haha 获得 列表里的东西。

2018年7月12日星期四

Other - Asynchronous "Async"



Asynchronous 同步


什么是 Async? 我相信很多人都看到可是不懂他什么意思. 有的人说 Async 和 Sync 应该是一样的东西吧?某些方面上是一样 但 某方面上又不一样.

有些老外用简单的一句解释 "Async" : "Run off main thread" / "跑在主线程之外". 也就是所谓的多线程 让多个 Task 在一次 Run 完 且不影响 主线程的情况下,这个东西对 主线程Concept 的游戏来说 是很大的帮助 也就是 Minecraft.

举个例子:
Minecraft 我的世界 Server 在 生成世界的时候 全部玩家都会卡着不能动 可是能聊天 Chat 还能收到与发送讯息 这是因为多线程的关系。

主线程 - Server 在 生成世界中 ... ,玩家们正在游玩
聊天Thread - Chat 在 主线程外 玩家的讯息 发送与接受都能收到
当然一个时候只能做一个事情,伺服生成世界的时候 玩家是无法移动或者破坏方块之类的。
 通过这个 "Async" 就能达成 不影响游戏进程的情况下 做其他东西的处理,比如 生成世界,获取网页资料 / Request data from backend, 或者 也可以是 处理 database 的 资料进出.

这边我也分享两个好资料

  1. FastAsyncWorldEdit - https://github.com/boy0001/FastAsyncWorldedit
  2. TaskChain - https://github.com/aikar/TaskChain

FastAsyncWorldEdit 

简称 "FAWE" 大家都懂吧 这个插件 在 minecraft 能在让服务器不卡的情况下 处理 worldedit 的 process,当然他也做了 额外的一些 wrapper for asynchronous process,例如 AsyncWorld 来处理 创建世界 与 关闭世界。

TaskChain

这个应该没很多人懂但这个很有用. 喜欢lambda的人也推荐使用 ( Java 8 ). 

简单来说个例子 :
Database 读取 / Querying 需要时间 因 Minecraft 是个 主线程concept 的游戏 那些会耽误到玩家的 游玩进度 随时卡顿 所以 最好把 这些都丢去 多线程处理 让游戏顺畅进行 也不会破坏玩家的游戏体验
Example of TaskChain :

TaskChainFactory tcf; 
tcf.newChain()
    .asyncFirst( // 处理资料的进入 )
    .abortIfNull( // 资料获取失败 可以加入ErrorHandler)
    .sync( // 处理 游戏的进程 ) [ Bukkit 的东西需要在主线程 很正常 ]
    .execute()

就这样处理一个过程。这个作者也有推荐 为什么要使用 TaskChain 也给出例子 也给出很好的解释 大家能看看

Other - Random 随机数值



No automatic alt text available.

Random ( Pseudo Random Distribution [ PRD ] )

P ( N ) = C * N 
N = 次数
C = 固定的数值 有算法的
在指定N 次数后 P(N) > 1 也就是100%了
100 * C = Approximate C (这个 value for every time increment 的 机率 )

etc 10% 他每次就会增加1.5% 在机率
第一次就 1.5%
第二次就 3%
第三次就 4.5%
第67次 就肯定是 > 100% 了

这个东西我觉得会比原本的 new Random().nextInt(100) 的 机率来的好 在 伺服主 与 玩家之间 。伺服主 如果推出付费的配套或者抽奖物品 会获得一定次数的赞助币 后 玩家才会获得 稀有物品 算是某些经济上的平衡(?)。

在有些技能上 这个也能做出平衡 打击下就 触发一下 不会到 第一下 100% 或者 打几千下 不会出一下的问题

2018年4月25日星期三

Chapter 1 - ECE Principle 事件,条件,效果


请看了上一篇再来哟



ECE Principle ECE原理

什么是 ECE Principle ?它是我自订的一个原理用在 Skript上的,何为 ECE?
  1. Events 事件
  2. Conditions 条件
  3. Effects 效果
Skript脚本编程就有如你的人生什么事件达到了什么条件就会有什么效果。这个原理恰好跟Skript相同。接下来给你看个例子
on break of glass: // 事件  - 在打破玻璃的时候
    if player's name is "GlassMan": // 条件  - 玩家的名字是 "GlassMan"
        drop glass  // 效果  -  掉落玻璃

当然你的人生会有许多的条件许多的效果 也当然脚本里一个事件 多个条件与多个效果都能。刚刚说了关于 ECE Principle 过后还有两样 Skript 重要的东西是
  1. Expression 表达
  2. Types 类别
Expression的例子就在 上面的程式码里有 player's name 这是一个 expression 表达着 玩家的名称 当然你要放为 字体也能 只需要 "玩家的名字 %player's name%" 他就会return 玩家的名字.

Types 的例子也有在上面的程式码,glass 是 玻璃 你也能用 ID 来指定 但是不鼓励 (我也不知为啥 ),这就只是分类一些 物品,方式,方法,点击类型之类的.

Variables 变数

那五个都理解了后就能到 最重要的部分了 Variables 变数,变数是可以 数值可以让你随意改变的 所以才叫变数。在 Skript 的变数只有两种类型 "列表变数" 与 "变数"  和 两种 存档方式 "暂存变数" 与 "永存变数"。

永存变数

{variables} // 这类型的变数通常会列为 Global Variables 全球变数 因为设定了到哪都能用 {list_variables::*} // 这如上只是他是 列表 能存档多个 变数 这样的变数都会存档在一个在 /plugins/Skript/variables.csv 的档案里面

暂存变数

{_variables} // 这样的呢就是 他们只能在你 宣告它的 那个事件 或条件里使用 {_variables::*} // 暂存也能用 列表变数 这样的变数在你的事件运行完之后就会自动删除了

善用指定变数

若以以上的设定要设定 指定变数在指定玩家身上的话 能考虑使用 {var.%player%} 用 player's name 或者 player's uuid 来指定 就能做到每个玩家都有自己的变数,这边有点像 Object的逻辑,就如每个object有自己的特殊名字或者id。

Options 设置

这算是一个原设定,只要写了就能在脚本里面使用,就能在脚本里无限处使用。

options:
    π: 3.142   // 字体 : 数值 或 字体 之类的设定

command /test:
    trigger:
        send "{@π}"

设定了 options了过后要采用它就用 {@名字} 他就会replace成 那个变数 这种情况不需要 %% 让他宣告成字体 直接 {@π} 就可以了.

Startup & Unload Skript 读取与卸载脚本

这我没什么例子 我就拿了 Skript原作人的例子

on script load:  // 脚本读取的时候
 set {running.%script%} to true
on script unload:  // 脚本卸载的时候
 set {running.%script%} to false

善用这两个事件 设定读取和释放不必要的变数之类的是很不错的哟,因为有些人只是卸载一些 脚本 不是所有 所以 没设定的话东西都会残留在伺服 造成卡顿。

最后

如你说见 脚本只需要要 英文语法好就很好开始了 当然 ECE Principle 做到很好的话 就能很好的 开发脚本咯 ~ 。如果以上的东西你都会了 那很好你已经成为了脚本师!欢迎加入爆肝党!

请耐心等待续篇 (Chatper 2 - 指令与循环 Command and Loops)

2018年4月24日星期二

Chapter 1 - Java Plugin 插件


请看了上一篇再来哟




我个人是使用 Esclipe IDE 所以图片与影片都是 Esclipe IDE 的。IDE差别不怎么大 只是介绍如何开新Project & Package & Classes。

进入正题 > 开启IDE会出现类似下图的 界面

接着点开左边右上 File 下面的小窗口就会出现应该要有的 工具栏 之类的, 过后在 Package Explorer栏里面 右键点击 就会出现一个列表选择 New -> Java Project 入下图1 就会出现类似图2的界面
图 1
图 2
ProjectName 输入你要的插件名字或者别名 JRE 里 选择 Use an environement JRE: 就好 若要指定其他版本的 Java 才选择第二个 之后Finish 就创建 Project 完成!

创建完毕后就会出现这样的 Project 接着就加入 Minecraft的 Server.jar

如右图 Configure Build Path 的界面右边有个 Add External JARs 然后选择 下载了的 Server.jar

加了Jar过后就能开始编程了 右键点击 src 文件夹然后 选择 New > Package 写入你自己的 package名字通常都是 me.oska.tutorial 这个格式 me.名字.插件名 看你个人喜好怎么放

加入了Package后再右键点击package 后 选择 New > Class 写入你的Class的名字 因为是 MainClass 所以我就放 TutorialMain.
开了之后就会有这样的界面 界面上会有 package 就是你创建了的 package 与 public class TutorialMain 是你创建了的 class 的name

public 算是 Modifier 而会有两种Modifier
  • Access Control Modifiers
  • Non Access Modifier

Access Control Modifier

这个Modifier 分别为 四个等级 package 里的 classes 使用 ( internal ) , class 自己使用罢了 ( private ) , 能给全世界使用 包括别人加入你所创建的插件档案 ( public ) , package 和 subclasses 使用罢了 ( protected )
  • internal
  • private
  • public
  • protected

Non Access Modifier

这个Modifier 分别为四种  static modifier for 创建 method 和 variables, final modifier for finalizing ( 最终化 ) classes, method 与 variables  也就是将它固定 所以不能被更改 一旦 finalize了过后, abstract modifier for 创建 abstract classes and methods 让别的classes 能 extends (继承)它所拥有的 methods. synchronized and volatile modifier for 多线程编程使用。
  • static
  • final
  • abstract
  • synchronized
  • volatile

讲解了Modifier过后 就到 extends , implements 和 import的解释了 如右图

import 的意思是进口 也就是 让插件能使用 该class的 method 之类的

extends 的意思就是继承 也就是让插件继承 abstract class 的 method (上面有说到)但是记得 一个 Class 只能extends 一个 Abstract Class

implements 的意思就是链接接口 也就是 让插件 链接该接口并且将他用于自己的方式 implements 是for interface class , 一个 class能 implements 很多个 interface


介绍完毕就是进入正题 开发插件如上图 extends 了 JavaPlugin 的 Class 你就能 开Method onEnable() 和 onDisable() 这两个method 的用途是 当插件读取时 与 当插件禁用时 而它们上面的 @Override 叫 annotation 都带有 '@' 这个符号而 Override 是 覆盖的意思 在这边就是 覆盖掉原本的 onEnable 和 onDisable method.

温馨提示 :
Overload 和 Override 是不一样的东西记得
一个是 过载 一个是 覆盖不要搞错哟

这次我们会先学习插件的基本如何制作一个插件所以编程部分比较少,如下图写入 读取时 发送讯息到 后台 与 移除时 发送讯息到后台

package me.oska.tutorial;

import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;

public class TutorialMain extends JavaPlugin 
{
 @Override
 public void onEnable()
 {
  Bukkit.getServer().getLogger().info("Tutorial Plugins Loading ...");
 }
 
 @Override
 public void onDisable()
 {
  Bukkit.getServer().getLogger().info("Tutorial Plugins Loading...");
 }
}

我们用 Bukkit.getServer().getLogger().info(string str) 来发送讯息到后台 ,你会好奇为什么 有些 method能直接 Bukkit.getServer() 有些不能。因为 static class 能直接使用 例如 Bukkit.getServer() 而 普通的 class呢 必须要有 object 存在 例如 new Bukkit().getServer() new 这个只要就是创建 Object使用的.

写入了之后 你能到 Project 那个文件夹 点击 右键 New > File > plugin.yml . 每个插件都必须要有一个 plugin.yml 来读取 因为原设是读取 plugin.yml 里的一些设定 才能读取这个插件,许多设定也能在 plugin.yml 设定 例如 permission, commands, depends, 之类的. 下图就是个 plugin.yml 的例子

main: me.oska.tutorial.TutorialMain
name: TutorialPlugin
version: TUTORIAL-0.1
description: description of the plugins
author: Oskang09
authors: [Oskang09, iRegalia]
website: https://oskang09.blogspot.my
depend: [WorldEdit, Towny, AnotherPlugin]
prefix: TutorialLogging
softdepend: [Essentials, Vault, AnotherPlugin]

main : 是最重要的东西 插件读取的 MainClass 位置 必须连 package也写进去 但是不需要任何的 extension 例如 me.oska.tutorial.TutorialMain

name :生成的 DataFolder() 的名字会是这个名字 这东西过后会说到在 Chapter 4

version :插件版本 这你能以你喜欢的写 例如 v0.1 , v0.2 之类的

description : 插件的介绍 一样随你怎么写

author : 作者的名称咯

authors : 可能插件是多个作者的就能用这个 格式如上图

website :网页网址

depend :依赖于某个插件 必须写对插件名 一旦插件不存在 你的插件就会拒绝读取 写错就无效了 (当然也能用 编程的 方式探测)

prefix : 插件每一次的 log , info , warning 显示的前缀

softdepend :你的插件会在这些插件读取后才进行读取 (可能要依赖那些插件做一些设定才需要这个softdepend)

写好了之后就是 如何 Export JARs 你到 Project 点击右键 然后 Export > Java > JAR file 选择了后选择 next 然后 写入export的路劲 与 名字到  JAR file : 的格子 然后点 Finish. (如下图)


Export好了之后就是开启你的伺服器 并且等待他读取。

如果显示和上图一样的东西就代表你完成了第一个插件!欢迎加入爆肝党(?)好像不是这样,你也能到我的 Github 查看源代码。

请耐心等待续篇 (Chatper 2 - Registering Command 加入指令)