Snake1999
本帖最后由 Snake1999 于 2014-2-6 16:44 编辑

P o c k e t M i n e - M P
          
              


Now, let's begin.
【前言】

      咳咳,大家好,我是二菌。今天我给大家带来的就是我的PM插件制作教程。
      PM,全称PocketMine-MP,是PE服务器软件中最流行的一种,用PHP编成,尤其以插件的多样性吸引了大多数PE腐竹的目光。PM发布以来,网上插件层出不穷,包括多世界插件、防作弊插件等等许多种类。PM插件对于菜鸟来说似乎遥不可及,只能瞻仰着各路大神的插件漫天飞舞。但是,制作插件真的真的真的很困难吗?
      不是的。PHP我认为是软件中BASIC、E语言以外最简单的语言,多用性、可塑性很强,特别好懂。学习一门像PHP的语言看起来似乎遥不可及,但是你一旦用心,就能和大神们一起创造。学习插件和玩PE一样,创造性强,没有做不到,只有想不到。相信屏幕前谁问你既然有一颗PE的心,那么一定能学好PM插件的。
      你想制作一个自己的插件吗?如果是,又不知如何入手,那么让二菌我来引导你 ——


【首先,你得会开服务器】

开PM服务器教程网上挺多,可以去翻翻,可以参考下面的教程:
http://www.mcbbs.net/thread-101500-1-1.html

如果真看不懂,让二菌我来教你——
图片来源:https://github.com/PocketMine/PocketMine-MP/wiki/Setting-up-a-Server

第一步,搭建服务器环境
方法1【荐】:

方法2:

ok , 第一步完成

第二步,运行服务器

双击start.bat即可开服。

有同学可能注意到控制台的字是彩色的,如果发现自己的字不是彩色的,说明你的bin/mintty.exe没有找到,可以去下载我的PHP5.4库试一试,在方法1里面。{:10_492:}
服务器的其他指令我这里就不多讲了,可以去网上查一下。


【其次,你得会玩PHP】
其实网上PHP教程也很多,这里有几个可以看看
http://www.w3school.com.cn/php/index.asp
http://www.php100.com/html/shipinjiaocheng/
真的看不懂?没事,别忘了这篇帖子是二菌写的...{:10_505:}
1.PHP的基本语法

2.PHP的变量

3.PHP的字符串

4.PHP的运算符

5.PHP的数组

6.PHP的基本语句块【If Switch 和循环语句】

7.PHP函数

8.PHP class类

孩子,你看到这里了,说明你对于PHP玩的差不多了,为制作一个PM插件已经打好了基础,往下看吧。



【最后,再想着做插件】

大家上面看完了吗?(众:看~完~了~)
那好,我们就开始做插件吧。

接下来,我们以SignTp插件作为目标,一步一步完成。
我们的插件要可以:
> 扔一个牌子就能传送到目的地!
> 有自动更新功能!
所以它需要:
> 有一堆命令
> 检测一堆事件
> 读取一堆配置文件
> 等等......
这看起来似乎很难的样子,让我们一步一步制作完毕。

0.一些准备

1.你的第一个PM插件
> 无中生有的第一步

2.给你的插件添加命令
> 键盘敲一敲,服务器嗨起来

================== 坑 线 =================
3.为你的插件检测事件
> 石头:现在你还你敢敲我?

  

【附录】

1.PM常见命令

2.PM数据类型

3.PM对象

======================== 坑  线 ==========================
4.PM API解析【已更API11】

来自群组: PocketMine开发&讨论
2021.12 数据,可能有更多内容
P o c k e t M i n e - M P
       

Now, let's begin.【前言】
   咳咳,大家好,我是二菌。今天我给大家带来的就是我的PM插件制作教程。
   PM,全称PocketMine-MP,是PE服务器软件中最流行的一种,用PHP编成,尤其以插件的多样性吸引了大多数PE腐竹的目光。PM发布以来,网上插件层出不穷,包括多世界插件、防作弊插件等等许多种类。PM插件对于菜鸟来说似乎遥不可及,只能瞻仰着各路大神的插件漫天飞舞。但是,制作插件真的真的真的很困难吗?
   不是的。PHP我认为是软件中BASIC、E语言以外最简单的语言,多用性、可塑性很强,特别好懂。学习一门像PHP的语言看起来似乎遥不可及,但是你一旦用心,就能和大神们一起创造。学习插件和玩PE一样,创造性强,没有做不到,只有想不到。相信屏幕前谁问你既然有一颗PE的心,那么一定能学好PM插件的。
   你想制作一个自己的插件吗?如果是,又不知如何入手,那么让二菌我来引导你 ——


【首先,你得会开服务器】
开PM服务器教程网上挺多,可以去翻翻,可以参考下面的教程:
http://www.mcbbs.net/thread-101500-1-1.html

如果真看不懂,让二菌我来教你——
图片来源:https://github.com/PocketMine/PocketMine-MP/wiki/Setting-up-a-Server

第一步,搭建服务器环境
方法1【荐】:
这里我用Windows 2008 Server演示一下
1.先去下载 PHP5.4库
   - 原帖下载地址貌似被墙,我这里有一个: 【点我!】
2.然后去下载ZIP的源代码
    - 先打开官网 www.pocketmine.net 看到这里

    - 然后往下看,点这里下载

    - 下载完和刚才的PHP库解压后放在一起

方法2:
直接去首页www.pocketmine.net下载EXE安装包,按照提示安装
不过貌似连接被墙了,大多数时候上不起来

ok , 第一步完成


第二步,运行服务器

双击start.bat即可开服。

有同学可能注意到控制台的字是彩色的,如果发现自己的字不是彩色的,说明你的bin/mintty.exe没有找到,可以去下载我的PHP5.4库试一试,在方法1里面。{:10_492:}
服务器的其他指令我这里就不多讲了,可以去网上查一下。


【其次,你得会玩PHP】其实网上PHP教程也很多,这里有几个可以看看
http://www.w3school.com.cn/php/index.asp
http://www.php100.com/html/shipinjiaocheng/
真的看不懂?没事,别忘了这篇帖子是二菌写的...{:10_505:}
1.PHP的基本语法
> PHP程序格式
作为一个PHP程序,PHP要求每个程序以"<?php"开头,"?>"结尾,即:

代码:

  1. <?php

  2. ?>
- PocketMine的插件作为大的PHP程序的一个片段,结尾可以不用输入,你愿意的话输入也可以。
&gt; 给你的程序添加注释
PHP的程序有时候需要用到注释来解释你的程序,毕竟程序是写给人看的{:10_509:}
注释有两种:单行注释//和注释块/*..*/,注释是写起来给人看的,不会在机器里面运行。看这个例子:

代码:

  1. <?php
  2. /* 注释块!!!
  3.     这里不会被运行!
  4. */

  5. //我是单行注释
  6. ?>
这个程序运行后什么都不会发生。{:10_522:}
第一讲完
2.PHP的变量


&gt; 变量的基本特征
PHP的变量和其它语言类似,也可以存储一个数字、字符串或者其它东西,并且可以在设置、赋值后重复调用。
不过,PHP是拜金主义者,它的每个变量调用和设置时都要把$放在变量名字前面(众:LZ你怎么说话的{:10_498:})
我们谈论变量时,一般用变量名字来谈论名字,而用$加上变量名字来代表变量本身。
变量名字必须符合以下规则:
- 变量名字必须以字母或者下划线&quot;_&quot;开头,如&quot;abc&quot;&quot;_1a&quot;是可以的,但是&quot;1a&quot;&quot;量a&quot;&quot;#123&quot;是不可以的。
- 变量名字必须只含数字字母或者下划线,所以&quot;a变量&quot;&quot;b#%&quot;是不可以的。
- 变量名字不能有空格
&gt; 设置变量的方法
PHP中设置变量的方法是这样的:

代码:

  1. $var = value;
var : 变量名字
value : 要设置的值
不要忘了PHP是拜金主义者,变量前面要加上$,漏掉后变量就无效了。
这样就可以设变量:
内容引用自shoghicp 的SimpleAuth:url找不到,抱歉

&gt; 变量的类型
PHP的变量类型是怎么设定的呢?我们看下面:
内容引用自W3SCHOOL:http://www.w3school.com.cn/php/php_variables.asp
PHP 是一门松散类型的语言(Loosely Typed Language)
在 PHP 中,不需要在设置变量之前声明该变量。
在上面的例子中,您看到了,不必向 PHP 声明该变量的数据类型。
根据变量被设置的方式,PHP 会自动地把变量转换为正确的数据类型。
在强类型的编程语言中,您必须在使用前声明变量的类型和名称。
在 PHP 中,变量会在使用时被自动声明。

但是有时候我们需要把变量类型转换掉,所以我们需要改变变量的类型。怎么改变呢?请使用(变量类型)$变量名字。看下面的例子:

代码:

  1. $a = "1";
  2. $b = "2";
  3. $c = (string)((double)$a + (double)$b);
这个例子里面,$a和$b是字符串,不能做数学的加减,所以我们需要把它转化为double类型来进行加减,然后转化会string类型输出。所以,它的输出是: 3
虽然PHP是一门松散类型的语言,但是变量的转化对它仍然重要。现将PHP中变量类型列表如下:
类型名字
存贮类型
例子
string
字符串
&quot;PHP Program!&quot;
boolean
布尔值
true
double
双精度数字(精度很高,±1.7E-308~±1.7E+308,含0)
1.23456
integer
整数(-2147483648~+2147483647)
123
array
数组(下面会讲)
array(123,456)
object
对象(可理解为class类等)
ServerAPI
关于变量类型的其它信息可以参考这里:http://zhidao.baidu.com/link?url=5-7dbhJRiJ1kvICLEDeIt9SKPL-xFhO2NcdfxXX7wfbq6W5VPz5uY1QiE-C8DCNvlzMnAzluVlxSrd2rtQmYN_
PHP变量的定义就这么简单,你学会了吗?
第二讲完
3.PHP的字符串
&gt; 字符串的特征
php中,字符串必须以&quot;&quot;开头和结尾,所以在字符串中一些字符的打法有些不同,详见下表:
字符
字符串中打法
&quot;
\&quot;
\
\\
换行
\n
送出CR
\r
tab跳位
\t
\n\r\t的详细解释可以参考这里:http://blog.csdn.net/jjmaiz/article/details/8212314
&gt; 字符串变量
php中,字符串变量是一类变量,允许你存储一条字符串在下面的例子里面,PHP把&quot;PHP Program!&quot;存进$str里面:

代码:

  1. <?php
  2. $str = "PHP Program!";
  3. echo $str;
  4. ?>
上面代码的输出 :
PHP Program!
现在,我们来试试用代码来处理我们的字符串。
&gt; 并置运算符(Concatenation Operator)
在PHP中,并置运算符只有一个,它就是 —— .
别小看这个小圆点,TA可以把两个字符串连接起来!看这儿:

代码:

  1. <?php
  2. $a = "1";
  3. $str = "abc".$a."def";
  4. echo str;
  5. ?>
上面代码的输出: abc1def不是连起来了呢?


再看下面的例子:
内容引用自PocketMine Team的PocketMine-MP/src/ConsoleAPI.php:www.pocketmine.net

红色:字符串1
蓝色:字符串2
红色和蓝色之间的部分: 并置运算符 .
这样&quot;console.command.&quot;和$cmd就连接起来了,如果$cmd = &quot;abc&quot;那么连起来的值就是&quot;console.command.abc&quot;


字符串的函数还有很多,可以去看这里:http://www.w3school.com.cn/php/php_ref_string.asp ,二菌就先讲一个:


&gt; strlen() 函数
strlen() 函数用于计算字符串的长度。我们来计算&quot;PHP Program!&quot;的长度:

代码:

  1. <?php
  2. echo strlen("PHP Program!");
  3. ?>

上面代码的输出: 12
&gt; 其它函数在这里可以找到:http://www.w3school.com.cn/php/php_ref_string.asp
第三讲完
4.PHP的运算符
&gt; 运算符
运算符能对值进行运算。我把PHP完整的运算符列表于此:
- 算数运算符
运算符
说明
例子
返回
+
求两个值的和
x=1+1;
x=2
-
求两个值的差
x=2-1;
x=1
*
求两个值的积
x=2*2;
x=4
/
求两个值相除的商
x=6/3;
x=2
%
求两个值相除的余数
x=7%3;
y=9%3;
x=1
y=0
++
让一个值自加1
x=1;x++;
x=2
--
让一个值自减1
x=3;x--;
x=2
+-*/小学老师都教过吧,不用说明了。% 就是求余数,小学老师好像也教过的,不过要注意左右两边不可以是小数。
++ 就是自加,让一个变量自加1,-- 类似。
大家都会了吗?{:10_521:}
- 赋值运算符
运算符
说明
例子
返回
=
赋值(貌似说过)
x=2;
x=2
+=
自添加
x=3;x+=4;
x=3+4=7
-=
自减去
x=5;x-=3;
x=5-3=2
*=
自乘
x=2;x*=3;
x=2*3=6
/=
自除以
x=6;x/=3;
x=6/3=2
%=
得到余数
x=7;x%=3;
x=7%3=1
.=
在末尾加上
x=&quot;1&quot;;x.=&quot;2&quot;
x=&quot;1&quot;.&quot;2&quot;=&quot;12&quot;
不用解释了吧= =


- 比较运算符
运算符
说明
例子
返回
==
是否相等
1+1==3
false
!=
是否不等
1+1!=3
true
&gt;=
是否大于等于
2&gt;=3
false
&lt;=
是否小于等于
2&lt;=3
true
&gt;
是否大于
1&gt;2
false
&lt;
是否小于
1&lt;2
true
比较运算符里面需要注意的是大于等于和小于等于,先写&gt;&lt;,再写=,别弄反了(众:&gt;_&lt;)
PHP里面还可能用到类型的比较,这时候我们可以用下面含有类型的比较运算符:
运算符
返回true条件
例子
返回
===
类型相等
值相等
2===&quot;2&quot;
2===2
false
true
!==
类型不等
值相等
2!==&quot;2&quot;
2!==2
true
false

- 逻辑运算符
运算符
说明
例子
返回
&amp;&amp;
(1==1)&amp;&amp;(2==2)
true
||
(1&gt;2)||(3&lt;2)
false
!
!(1&gt;2)
true
逻辑运算符中和或非的定义可以参照这里:http://baike.baidu.com/view/397415.htm


- instanceof
还有一种运算符叫作instanceof,它能判断一个变量是否属于这个类型。例子:

代码:

  1. echo (string)("2" instanceof double)
返回值:0,字符串&quot;2&quot;类型是string不是double,所以返回false即0。


PHP的运算符在实际的判断中有很大的应用,将在下面的If中提到。
第四讲完
5.PHP的数组

&gt; 数组是什么?
内容引用自W3SCHOOL:http://www.w3school.com.cn/php/php_variables.asp
在使用 PHP 进行开发的过程中,你可能需要创建许多相似的变量。无需很多相似的变量,你可以把数据作为元素存储在数组中。
数组中的元素都有自己的 ID,因此可以方便地访问它们。

所以,我们可以方便地通过数组来规定一系列变量。


&gt; 数组的规定
数组存储的每个元素都带有一 ID 键,ID键可以是数字,也可以是以单引号开头结尾的一串字符(例如'abc')。数组有两种规定方法:
- 直接规定:

代码:

  1. $arr = array("a",1,true);
这时ID键会自动分配。从上面我们看到,数组每个元素不一定都是一种变量。
ID键自动分配的结果就是会以从0开始给每个元素分配ID键。所以上面的数组可以表示为:
ID键
0
&quot;a&quot;
1
1
2
true

当然,我们在定义时也可以自己规定ID键:

代码:

  1. $arr = array(
  2. "str" => "a",
  3. 0 => 1,
  4. "bool" => true,
  5. );
这里要注意的是,=&gt;不要写错成=,逗号不要写错成分号。
也可以在后续的代码里面自己添加ID键:

代码:

  1. $arr['str'] = "a";
  2. $arr[0] = 1;
  3. $arr['bool'] = true;
这里我们看到了调用数组的一个方法:$变量名字[ID键]
当然,调用时ID键可以可以是变量,例如:

代码:

  1. $arr = array(
  2. "str" => "a",
  3. 0 => 1,
  4. "bool" => true,
  5. );
程序输出:a
当然,嵌套的数组也是OK的,例如:

代码:

  1. $a = array(
  2. 0 => array(
  3.     "x" => "a",
  4. ),
  5. 1 => 2,
  6. );
这时我们把嵌套的数组叫做多维数组。
多为数组的调用方法:$数组名字[$ID键][$ID键][$ID键]......
例如:

代码:

  1. $a = array(
  2. 0 => array(
  3.     "x" => "a",
  4. ),
  5. 1 => 2,
  6. );
  7. echo $a[0]['x'];
输出: a


&gt; print_r()函数
print_r($数组名字)可以直接打印出数组,例如:

代码:

  1. $a = array(
  2. 0 => array(
  3.     "a",
程序输出:
Array
(
[0] =&gt; Array
(
    [0] =&gt; a
    [1] =&gt; b
    [2] =&gt; c
)
[1] =&gt; 2
)
于是数组所有的内容都出来了~!神奇吧~{:10_521:}
数组的其它命令可以参考这里:http://www.w3school.com.cn/php/php_ref_array.asp
第五讲完
6.PHP的基本语句块【If Switch 和循环语句】

&gt; If、else和elseif
If、else和elseif这三个指令可以允许你判断,然后为判断的结果做出不同的动作。
- 如果你要在一个条件成立时使用代码,请用If。If的用法:

代码:

  1. if(判断条件) 命令;
如果命令为多行,你应该用{}括起来,像这样:

代码:

  1. if(判断条件){
  2. 命令;
  3. }

- 如果你要判断一个条件成立或不成立是使用两个不同的代码,请用If和else。这两个东西的用法:

代码:

  1. if(判断条件) 为true时执行命令;
  2. else 为false时执行命令;
如果命令为多行,你也应该用{}括起来,像这样:

代码:

  1. if(判断条件) {
  2. 为true时执行命令;
  3. }else{
  4. 为false时执行命令;
  5. }
- 如果你想要多个条件成立之一时运行不同的代码,请用If、else和elseif。用法:

代码:

  1. if(判断条件1) 判断条件1为true时命令;
  2. elseif(判断条件2) 判断条件2为true时命令;
  3. elseif(判断条件3) 判断条件3为true时命令;
  4. .......
同样地,我们应该这样设置多行:

代码:

  1. if(判断条件1) {
  2. 判断条件1为true时命令;
  3. }elseif(判断条件2){
  4. 判断条件2为true时命令;
  5. }elseif(判断条件3){
  6. 判断条件3为true时命令;
  7. .......
  8. }else{
  9. 判断条件全部为false时命令;
  10. }
嗯..讲的太多是不是有些晕呢?(众:{:10_499:})那么我们来看一下例子。请看这儿:
内容引用自shoghicp的WorldEditor:url找不到,抱歉

这是一个简单的if else判断句。
红色:判断条件
蓝色:红色为true时的执行语句
绿色:红色是false时的执行语句
也就是说,如果$totalCount &gt; 524288,那么$send = false;如果!($totalCount &gt; 524288),那么$send = true.
再看这儿:
内容引用自Snake1999的SignTp:http://www.mcbbs.net/thread-226762-1-1.html

红色:判断条件
蓝色:红色为true时的执行语句
粉色:判断条件2
黑色:粉色为true时执行的语句
灰色:判断条件全都是false时执行的语句
也就是说,如果(double)$ver1e[1]&lt;(double)$ver2e[1],那么返回值就是&quot;2&quot;;如果....;如果(double)$ver1e[3]=(double)$ver2e[3],那么返回&quot;0&quot;;如果全都是false,那么返回&quot;1&quot;。


现在懂了吗?{:10_496:}
&gt; ... ? ... : ... 语句块
还有一种..?..:..语句块,方法与单行if..else类似:

代码:

  1. $变量 = (判断条件)?为true是返回值 : 为false时返回值 ;
例子:

代码:

  1. echo (1+1==3)?"1+1竟然等于3":"1+1不等于3";
返回:1+1不等于3


不知大家发现了没,如果用elseif重复判断一个值是否为另一个值,会让代码过于麻烦,这时用搜寻语句块switch即可解决问题。{:10_525:}
&gt; Switch
如果你希望有选择地执行若干代码块之一,可以使用 Switch 语句,这样可以避免冗长的if..elseif..else代码块。
- Switch 的工作原理
内容引用自 w3schoool:http://www.w3school.com.cn/php/php_switch.asp
1.对表达式(通常是变量)进行一次计算
2.把表达式的值与结构中 case 的值进行比较
3.如果存在匹配,则执行与 case 关联的代码
4.代码执行后,break 语句阻止代码跳入下一个 case 中继续执行
5.如果没有 case 为真,则使用 default 语句

所以,正确的 Switch 应该是这样的:

代码:

  1. switch(表达式){
  2. case 值1:
  3. 表达式=值1时执行的代码;
  4. break;
  5. case 值2:
  6. 表达式=值2时执行的代码;
  7. break;
  8. default:
  9. 表达式都不等时执行的代码;
  10. break;
  11. }
  12.    
如果我们希望在表达式和两个值分别相等时都执行相同的代码,我们可以这样做:

代码:

  1. switch(表达式){
  2. case 值1:
  3. case 值2:
  4. 表达式=值1或值2时执行的代码;
  5. break;
  6. default:
  7. 表达式都不等时执行的代码;
  8. break;
  9. }
这样就方便多喽~{:10_492:}我们看这里:


内容引用自Snake1999的SignTp:http://www.mcbbs.net/thread-226762-1-1.html



这样我们可以看到,输入/st help [命令名字] 里面命令名字不同时,会返回不同的帮助文字,而且命令名不是下面列出的就会返回总的命令帮助。如果用if..else那会有多麻烦!{:10_499:}
还好PHP的发明者给我们带来了switch{:10_512:}
&gt; 循环语句
PHP中循环语句有4种:while、do..while、for、foreach。喝杯茶,我们继续 ——
&gt; while 和 do..while
- while 和 do..while 的用法

代码:

  1. while(表达式) 执行的代码;

  2. while(表达式){
  3. 执行的代码;
  4. }

  5. do{
  6. 执行的代码;
  7. }
  8. while(表达式);

- while 和 do..while 的区别
while 和 do..while 都会在表达式成立的情况下重复执行代码,但是不同的是:
while 先判断表达式,后执行代码,再重复;
do..while 先执行代码,后判断表达式,再重复。
联想着判断语句的位置就会很好记。
&gt; for
已经确定了代码块的重复执行次数时,可以使用 for 语句。
- for语句的原理:
1.先执行定义来定义循环所需变量和初始值,
2.然后执行一次代码,
3.再然后判断条件,如果为false则跳出循环
4.最后执行一次增量来增加变量。
- for语句的用法:

代码:

  1. for(定义;条件;增量){
  2. 代码;
  3. }
有点难懂?我们来看下面的例子:

代码:

  1. for(i=0;i<3;i++){
  2. echo i;
  3. }
这个代码先会定义i=0,然后echo i;,然后判断i是否小于3,最后执行i++;。所以它的输出是:012
懂了吗?那我继续讲{:10_508:}
&gt; foreach
foreach 语句用于循环遍历数组。用法有两种:

代码:

  1. foreach($数组名字 as $数组元素){
  2. 代码;
  3. }

  4. foreach($数组名字 as $数组ID键 => $数组元素){
  5. 代码;
  6. }
比较好懂,是吗?
我们先看例子:
内容引用自ljyloo 的 TpMultiWorld:http://www.mcbbs.net/thread-148950-1-2.html

在这里foreach可以遍历$this-&gt;worlds数组,每个元素存储到$w中,然后达到列出所有世界的效果。


呼~终于讲完了{:10_508:}你懂了吗?
第六讲完
7.PHP函数

&gt; 函数
函数是一种可以在任何被需要的时候执行的代码块。每个函数需要有一个名字来区分,名字的命名规则同变量名字的命名规则(忘了?返回去看看)。简单的函数创建需要以下几个步骤:
内容引用自W3SCHOOL:http://www.w3school.com.cn/php/php_functions.asp
1.所有的函数都使用关键词 &quot;function()&quot; 来开始
2.命名函数 - 函数的名称应该提示出它的功能。函数名称以字母或下划线开头。
3.添加 &quot;{&quot; - 开口的花括号之后的部分是函数的代码。
4.插入函数代码
5.添加一个 &quot;}&quot; - 函数通过关闭花括号来结束。

也就是说,一个简单的函数应该是这样的:

代码:

  1. function 函数名字(){
  2. 代码;
  3. }
函数怎么调用呢?下面是一个例子:

代码:

  1. function pr(){
  2. echo "PHP Program!\n";
  3. }
  4. pr();
  5. pr();
从这里可以看到,我创建了一个名为pr的函数,可以输出abc然后换行,我可以直接用pr();来调用函数输出。于是上面的程序输出是:
PHP Program!
PHP Program!
懂了吗?{:10_505:}
&gt; 带有参数的函数
我们例子里面的函数只是是一个非常简单的函数。它只能输出一个静态的字符串{:10_506:}。
加上参数后会让函数变得多姿多彩{:10_508:}。
通过可以添加参数,我们向函数添加更多的功能。参数类似一个变量。
你可能注意到了,函数名称后面有一个括号,比如 pr()。参数就是在括号中规定的。就像这样,我们就可以更改输出的字符:

代码:

  1. function pr($str){
  2. echo $str." Program!\n";
  3. }
  4. pr("PHP");
  5. pr("Not a C");
这里函数的参数是$str,在使用函数时输入参数,就可以实现特定的功能。比如上面的程序输出是:
PHP Program!
Not a C Program!
如果要求用户没有输入某个参数是也能实现特定的功能,我们要规定参数的默认值,就像这样:

代码:

  1. function pr($str = "PHP",$str2 = " Program!"){
  2. echo $str.$str2."\n";
  3. }
  4. pr(,"Program!");
  5. pr("Not a C");
这里可以看到第一个调用没有输入第一个参数,但是pr的定义里面规定了第一个参数的默认值,所以第一个参数就编程了&quot;PHP&quot;,于是程序的输出是:
PHP Program!
Not a C Program!
第二个调用没有输入最后一个参数,这时候可以省略前面的逗号。
函数的参数我们学到这里。懂了吗?{:10_525:}


&gt; 返回值
函数大家初中都学过,带入不同的x值会有不同的y值,为什么PHP的函数不能有函数值呢?于是,PHP的返回值就出现了{:10_500:}
返回值应该怎么设置和调用呢?看这儿:

代码:

  1. function add($a,$b){
  2. return $a+$b;
  3. }
  4. echo add(1,2);
可以看出,return 值; 是返回值得一个关键。返回了一个值,就可以对它进行运算,甚至调用到其它函数里面,你可以像变量一样折腾一个函数的返回值,一样可以用echo打印输出。所以这个程序的输出是: 3
我们来看下面的例子:
内容引用自PocketMine Team的PocketMine-MP/src/ConsoleAPI,部分折叠:www.pocketmine.net

红色:参数定义
蓝色:返回命令
从这里看到,run这个函数定义了几个参数,都设定了默认值,又加了return来返回值,是一个比较复杂的函数了。
关于函数,就讲到这里。你懂了吗?{:10_531:}
第七讲完
8.PHP class类

没错,你看到这里的时候,没看的PHP的教程只有一节了。加油,把它看完!
&gt; class类
PHP中,有时候需要把零散的代码分成几个部分,于是,类的概念就诞生了。每个class,或者说类,由以下几个部分组成:
内容引用自kv516 的 PocketEssentials:http://www.mcbbs.net/thread-196684-1-1.html

红色:声明这是一个class
蓝色:class的名字
绿色:接口,右边的Plugin是接口名字(下面会讲),省略即为一般class
黑色:声明{}里面是class的内容。
粉色:class的内容
于是,这样一个class就声明好了。
&gt; 调用方法
在class内,我们用$this来代表class本身,所以:
- class内定义变量(非函数内定义)的调用方法为:$this-&gt;变量
- class内定义函数的调用方法为:$this-&gt;函数名字(参数)
但是函数内定义变量调用方法仍为$变量。看下面:

代码:

  1. class a{
  2. public $a = "a",$b = "This is b";
  3. public function get($isb){
  4.     if($isb){
  5.    return $this->b;
  6.     }else{
  7.    return $this->a;
  8.     }
  9. }
  10. }
这个例子里面$isb是函数内定义变量,调用方法仍为$isb,而class内定义变量$a、$b则需要用$this-&gt;a和$this-&gt;b来调用。
&gt; 跨class调用方法
我们在玩PHP的时候可能会用到调用另外一个class的变量或者函数,这时候我们需要这样做:
- 另外一个class的名字::变量
- 另外一个class的名字::函数(参数)
请看下面的例子:

代码:

  1. class a{
  2. public $a = "a",$b = "This is b";
  3. public function get($isb){
  4.     if($isb){
  5.    return $this->b;
  6.     }else{
  7.    return $this->a;
  8.     }
  9. }
  10. }
  11. class b{
  12. public function init(){
  13.     echo a::get(true);
  14. }
  15. }
这个例子里面在class a里面定义了get这个函数,如果我们运行class b的init函数,就会调用class a返回a::b的值,然后输出。所以这个程序的输出是:This is b
懂了吗?{:10_505:}
&gt; 使用范围
声明玩一个class后,一个问题出来了:class里面有些函数和变量仅适合class内部使用或者不希望公开,其它的一些希望公开,那该怎么办呢?我们可以使用public和private。
使用范围
说明
例子
public
公用(class以外可以调用)
public function init(){}
private
私有(仅class内可以使用)
private $api;
怎么理解呢?在定义函数和变量时前面加上public,就可以允许其它class用::来调用,private则不允许。我们看这里:内容引用自Snake1999 的 SignTp:http://www.mcbbs.net/thread-226762-1-1.html

这里不同的function都用颜色标出来了,但是里面一些变量不希望外面调用,所以是private。
&gt; class接口
【WARNING:前方高能,请无关人员迅速撤离!{:10_542:}】
什么是接口?接口允许你创建一类相似的class,以便调用。
class定义时接上这个接口,意味着需要含有接口规定的一切函数和变量。例如上面的Plugin就是一个接口,定义了一类相似的class。我们看这里:
内容引用自PocketMine Team的PocketMine-MP/src/PluginAPI:www.pocketmine.net

上面的例子规定了一个叫做Plugin的接口,然后创建了一个叫做DummyPlugin的类,这个DummyPlugin就是一个标准的Plugin,拥有接口规定的所有函数(含参数)和变量。如果缺少任何一个,就不是这个接口对应的class。
现在懂了吗?
第八讲完
孩子,你看到这里了,说明你对于PHP玩的差不多了,为制作一个PM插件已经打好了基础,往下看吧。




【最后,再想着做插件】
大家上面看完了吗?(众:看~完~了~)
那好,我们就开始做插件吧。

接下来,我们以SignTp插件作为目标,一步一步完成。
我们的插件要可以:
&gt; 扔一个牌子就能传送到目的地!
&gt; 有自动更新功能!
所以它需要:
&gt; 有一堆命令
&gt; 检测一堆事件
&gt; 读取一堆配置文件
&gt; 等等......
这看起来似乎很难的样子,让我们一步一步制作完毕。


0.一些准备
文本部分来自 http://www.mcbbs.net/thread-204040-1-1.html ,原帖为JS,其实PHP和JS编辑器是基本一样的。
&gt; 首先你需要装了PM服务端的电脑一台,用作开服务器(技术宅可以用安卓手机,详情右转【此处】
&gt; 然后你需要文本编辑器一只,这个种类繁多,下面我来推荐几个:
系统
软件
windows
notepad++,ultraedit(用Notepad的请受小神一拜)
linux
gvim
android
920文本编辑器,rootexplorer自带的文本编辑器
iOS
这个真没有= =
&gt; 其次是装了MCPE手机或平板一台,用作调试
1.你的第一个PM插件
&gt; 无中生有的第一步
&gt; 首先,你需要打开你的服务端然后关闭,我们会发现多了一些文件夹,我们的插件就要放在/plugins里面。

&gt; 然后,我们在/plugins里面创建一个php文件,名字可以最好能表现插件的名字等等。
这里我们以SignTp为例子,创建一个SignTp.php

&gt; 在编辑器里打开,没错,空的。
还记得上面PHP教程怎么说的?(众:php文件一定要以&quot;&lt;?php&quot;开头)好的,我们加上。
&gt; 作为一个PM的插件,我们需要用Plugin这个接口。看这儿:内容引用自PocketMine Team的PocketMine-MP/src/PluginAPI:www.pocketmine.net

这个接口说明了一个Plugin必备的三个函数,所以一般的PM插件格式是这样的:
内容引用自Snake1999 的 SignTp:http://www.mcbbs.net/thread-226762-1-1.html

天蓝:PM插件必备的注释部分。请看这里:   
    /*
    __PocketMine Plugin__
    name=PluginXXX  &lt;&lt;插件名字,会在控制台插件加载时显示
    description=   &lt;&lt;插件说明,貌似没用
    version=0.0.1    &lt;&lt;插件版本,会在控制台插件加载时显示
    author=XXX   &lt;&lt;插件作者,会在控制台插件加载时显示,也能说明版权
    class=PluginXXX   &lt;&lt;插件主类名字,和下面的class PluginXXX ....名字应该一样
    apiversion=11   &lt;&lt;插件兼容API版本,用逗号隔开(以后会讲)
  */
紫红:插件的主类。里面塞着插件大部分的内容。
深蓝:预处理插件时触发的function。这里需要定义api这个变量作为插件与服务器沟通的桥梁。
绿色:卸载时触发的function。&lt;&lt;Tip:这里的卸载是加载的反义词,不是删除!!
红色:插件加载时促发的function。

所以,一个空的插件应该是这样的:

代码:

  1. <?php
  2.  
  3. /*
  4. __PocketMine Plugin__
  5. name=PluginXXX
  6. description=
  7. version=0.0.1
  8. author=XXX
  9. class=PluginXXX
  10. apiversion=11
  11. */
  12.    
  13. class PluginXXX implements Plugin{
  14.   private $api;
  15.   public function __construct(ServerAPI $api, $server = false){
  16.   $this->api = $api;
  17.   }
  18.    public function __destruct(){}
  19.   public function init(){}
  20. }
保存,打开Start.bat,我们会看到:

没错,你的插件被加载了!{:10_509:} 虽然没什么用。{:10_494:}
赶紧放到SignTp里面,于是我们的代码是这样的:

代码:

  1. <?php
  2.  
  3. /*
  4. __PocketMine Plugin__
  5. name=SignTp
  6. description=
  7. version=0.0.1
  8. author=DreamWork Studio
  9. class=SignTp
  10. apiversion=11
  11. */
  12.    
  13. class SignTp implements Plugin{
  14.   private $api;
  15.   public function __construct(ServerAPI $api, $server = false){
  16.   $this->api = $api;
  17.   }
  18.    public function __destruct(){}
  19.   public function init(){}
  20. }
第一讲完
2.给你的插件添加命令
&gt; 键盘敲一敲,服务器嗨起来

&gt; 命令的用法命令能干啥?很多插件都需要命令。命令允许玩家在游戏中玩转插件,那么该怎么添加命令呢?
我们先看这里:
内容引用自kv516 的 PocketEssentials:http://www.mcbbs.net/thread-196684-1-1.html

红色:从api提供的方法注册一个命令
蓝色:命令的名字
粉色:命令在 &quot; /help 名字 &quot; 里面显示出的帮助文字
青色:激活命令后触发的function所在的类,一般用$this
棕色:激活命令后触发的function名字,下面要相同
那么如果我们需要让命令实现同样的功能,或者能让一般玩家可以用,该怎么办呢?
我们先看这里:
内容引用自ljyloo 的 TpMultiWorld:http://www.mcbbs.net/thread-148950-1-2.html

红2:注册一个相同的命令,即/w 和/tpw 有相同的作用,在下面command只需要规定/tpw即可。
粉2:要注册相同指令的目标指令,即未规定但要与元指令相同指令。
绿2:要注册相同指令的原指令,即规定要与目标指令相同的指令。
蓝2:注册一个一般玩家可以能用的指令
黑2:要注册一般玩家可以能用的指令命令的名字
于是,我们注册了一个命令,接下来要定义它的作用。我们继续看上面的图:
红1:要和注册指令右边的红圈相同,是激活命令后触发的function名字。
黑1:$cmd : 激活的命令名称,比如&quot;tpw&quot; &quot;listw&quot;等
蓝1:$params : 激活的子命令的名称
紫1:$issuer : 该玩家的Player对象
绿1:$alias : 这个命令的相同的指令,如&quot;tpw&quot; 的alias 是&quot;w&quot;,多个就是数组
其实这里$cmd等等名字改掉是可以的,但是$cmd这几个大家都习惯了,所以建议保留,详细看下面。
请看这里的例子:
    如果一个玩家输入了/tpw 123 456 789
    那么返回的$cmd = &quot;tpw&quot;
  $params[0] = &quot;123&quot;
  $params[1] = &quot;456&quot;
  $params[2] = &quot;789&quot;
  $issuer = 该玩家的Player对象(以后会讲)
  $alias = &quot;w&quot;
然后,加上一个switch,就可以让命令有作用喽~
就像这样:
内容引用自MinecrafterJPN 的 PocketMoney:http://forums.pocketmine.net/plugins/pocketmoney.63/

就这样搜寻以后/money指令就起作用了,赶紧拿来试试!

代码:

  1. <?php
  2.  
  3. /*
  4. __PocketMine Plugin__
  5. name=SignTp
  6. description=
  7. version=0.0.1
  8. author=DreamWork Studio
  9. class=SignTp
  10. apiversion=11
  11. */
  12.    
  13. class SignTp implements Plugin{
  14.     private $api;
  15.     public function __construct(ServerAPI $api, $server = false){
  16.     $this->api = $api;
  17.     }
  18.     public function __destruct(){}
  19.     public function init(){
  20.     $this->api->console->register("st", " : SignTp command.", array($this, "command"));
  21.   }
  22.     public function command($cmd, $params, $issuer, $alias){
  23.   switch($cmd){
  24.    case "st":
  25.   console("123");
  26.   break;
  27.     }
  28.   }
  29. }
保存,运行

在粉色部分,我们看到我们的插件被加载了,输入/st以后,console返回了123;在/? st里面也有了相应的解释。说明我们的命令起作用喽~
在命令执行的部分,我们可以使用return来返回一个值让使用者知道,所以上面代码可以改为:

代码:

  1. <?php
  2.  
  3. /*
  4. __PocketMine Plugin__
  5. name=SignTp
  6. description=
  7. version=0.0.1
  8. author=DreamWork Studio
  9. class=SignTp
  10. apiversion=11
  11. */
  12.    
  13. class SignTp implements Plugin{
  14.     private $api;
  15.     public function __construct(ServerAPI $api, $server = false){
  16.     $this->api = $api;
  17.     }
  18.     public function __destruct(){}
  19.     public function init(){
  20.     $this->api->console->register("st", " : SignTp command.", array($this, "command"));
  21.   }
  22.     public function command($cmd, $params, $issuer, $alias){
  23.   switch($cmd){
  24.    case "st":
  25.   return "123";
  26.   break;
  27.     }
  28.   }
  29. }
这样console返回就是 [CMD] 123 了。同样地,玩家也能使用/st来返回123。
&gt; 添加功能
我们玩过的插件命令功能绝没有输出123那么简单,这时候我们就需要发挥想象力,参考下面的附录可以看你能干啥,然后搭配好代码,就能让你的命令多姿多彩。
第二讲完
================== 坑 线 =================
3.为你的插件检测事件
&gt; 石头:现在你还你敢敲我?



第三讲完


【附录】
1.PM常见命令
&gt; console
console命令用于在控制台输出一条带现在时间的信息。
用法:console($message, $EOL = true, $log = true, $level = 1)
$message:信息的文本。
$EOL:决定是否在信息末尾加上PHP_EOL(换行)。默认true为加上。
$log:决定是否记录在console.log内。默认true。
$level:这个我也不知道= =谁来告诉我
- $message含下面的内容将会设置输出文字的颜色,下面是常用的颜色:
内容
颜色
FORMAT_AQUA
浅绿色
FORMAT_RED
红色
FORMAT_GRAY
灰色
FORMAT_YELLOW
黄色
FORMAT_RESET
黑色
所有的颜色可以在/src/utils/TextFormat.php里面查看。
添加颜色的方法:把内容用 . 连接到字符串里,如:

代码:

  1. console(FORMAT_RED . "RED" . FORMAT_RESET . "BLACK");
输出:[CMD] REDBLACK
&gt; API11的常见命令就这么一个= =待添加第一讲完
2.PM数据类型
&gt; Vector3
在PM插件制作时,很多时候会用到三轴坐标,于是Vector3就诞生了。{:10_525:}
- 定义方法

代码:

  1. $变量名字 = new Vector3(x,y,z);

这里new这个单词能告诉PHP要创建一个新Vector3类型的变量;xyz参数告诉PHP这个三轴坐标的位置。
- Vector3类型的常用方法
假如$var(1.4, -2, 3.6)是一个vector3对象,你可以......
使用方法
返回
作用
例子
返回值
$var
string
得到$var的三轴坐标
$var
&quot;Vector3(x=1.4,y=2,z=3.6)&quot;
$var-&gt;getX()
$var-&gt;getRight()
$var-&gt;getSouth()
variant
得到$var的x轴坐标
$var-&gt;getX()
1.4
$var-&gt;getY()
$var-&gt;getUp()
variant
得到$var的y轴坐标
$var-&gt;getY()
-2
$var-&gt;getZ()
$var-&gt;getForward()
$var-&gt;getWest()
variant
得到$var的z轴坐标
$var-&gt;getZ()
3.6
$var-&gt;getFloorX()
integer
得到$var的x轴坐标并取整
$var-&gt;getFloorX()
1
$var-&gt;getFloorY()
integer
得到$var的y轴坐标并取整
$var-&gt;getFloorY()
-2
$var-&gt;getFloorZ()
integer
得到$var的z轴坐标并取整
$var-&gt;getFloorZ()
3
$var-&gt;add ($x, $y, $z)
Vector3
将$var的三轴坐标加上对应值后返回
$var-&gt;add(1, -2, 3)
Vector3(2.4, -4, 6.6)
$var-&gt;add (Vector3 $x)
Vector3
将$var的三轴坐标加上$x的对应值后返回
$var2 = new Vector3(1, -2, 3);
$var-&gt;add($var2)
Vector3(2.4, -4, 6.6)
$var-&gt;subtract ($x, $y, $z)
Vector3
将$var的三轴坐标减去对应值后返回
$var-&gt;subtract(-1, 2,- 3)
Vector3(2.4, -4, 6.6)
$var-&gt;subtract (Vector3 $x)
Vector3
将$var的三轴坐标减去$x的对应值后返回
$var2 = new Vector3(-1, 2, -3);
$var-&gt;add($var2)
Vector3(2.4, -4, 6.6)
$var-&gt;multiply ($number)
Vector3
将$var的三轴坐标都乘$number并返回
$var-&gt;multiply(2)
Vector3(2.8, -4, 7.2)
$var-&gt;divide($number)
Vector3
将$var的三轴坐标都除以$number并返回
$var-&gt;divide(2)
Vector3(0.7, -1, 1.8)
$var-&gt;celi()
Vector3
将$var的三轴坐标都+1且取整后返回
$var-&gt;celi()
Vector3(2, -1, 4)
$var-&gt;floor()
Vector3
将$var的三轴坐标取整后返回
$var-&gt;floor()
Vector3(1, -2, 3)
$var-&gt;round()
Vector3
将$var的三轴坐标四舍五入后返回
$var-&gt;round()
Vector3(1, -2, 4)
$var-&gt;abs()
Vector3
将$var的三轴坐标取绝对值后返回
$var-&gt;abs()
Vector3(1.4, 2, 3.6)
$var-&gt;getSide($side)
Vector3
得到与$var相邻的一个坐标 *
$var-&gt;getSide(0)
Vector3(1.4, -3, 3.6)
$var-&gt; distance ($x=0,$y=0,$z=0)
double
得到$var与Vector3($x,$y,$z)的距离
$var-&gt;distance(0, 2, 3)
4.2801869118065
$var-&gt; distance (Vecrot3 $x)
double
得到$var与$x的距离
$var2 = new Vector3(0, 2, 3);
$var-&gt;distance($var2)
4.2801869118065
$var-&gt; distanceSquared ($x=0,$y=0,$z=0)
double
得到$var与Vector3($x,$y,$z)
的距离的平方
$var-&gt;distanceSquared
(0, 2, 3)
18.32
$var-&gt; distanceSquared (Vecrot3 $x)
double
得到$var与$x的距离的平方
$var2 = new Vector3(0, 2, 3);
$var-&gt;distanceSquared($var2)
18.32
$var-&gt;dot (Vecrot3 $x)
double
得到$var与$x点乘的结果
点乘是什么?【左转这里】
$var2 = new Vector3(0, 2, 3);
$var-&gt;dot($var2)
6.8
$var-&gt;cross (Vecrot3 $x)
Vector3
得到$var与$x叉乘的结果
叉乘又是啥?【右转这里】
$var2 = new Vector3(0, 2, 3);
$var-&gt;cross($var2)
Vector3(-13.2, -4.2, 2.8)
* 相邻坐标的定义见下:如果坐标方向按MC的定义且x轴向右z轴向前,即

那么:
$side值
表示XYZ坐标变化
相当于
0
x不变,y减一,z不变
下面
1
x不变,y加一,z不变
上面
2
x不变,y不变,z减一
后面
3
x不变,y不变,z加一
前面
4
x减一,y不变,z不变
左面
5
x加一,y不变,z不变
右面
有点乱是吗?(众:{:10_499:})其实Vector3还有很多方法,可以看/src/math/Vector3.php,这里只列出常用的一部分。有了Vector3,就可以方便地表示坐标了{:10_525:}&gt; PM API11常用的数据类型貌似就这一个了= =
第二讲完
3.PM对象
&gt; 对象是什么
对象(object)是一种变量类型,允许你调用程序内部特定的内容。比如,一个世界就是一个level对象,你的插件可以通过更改level对象来改变世界的信息。
&gt; Position对象
- 源文件位置(忘记指令之类的可以去翻源文件,接下来不再提示):/src/world/Position.php
- 特性
Position对象用于表达一个事物的坐标,包括xyz坐标和世界。
Position对象源文件的开头有这样一句话:

代码:

  1. class Position extends Vector3{
也就是说:Position其实就是一个Vector3对象,接口了Vector3{:10_496:},于是Vector3的方法和Position的方法是相似滴~
- 方法
如果$pos是一个Position对象,那么你除了可以用Vector3的方法,还可以——
方法
类型
作用
$pos
string
返回这个点的三轴坐标
&quot;Position(level=xxx,x=x,y=y,z=z)&quot;
$pos-&gt;x
$pos-&gt;y
$pos-&gt;z
variant
返回这个点某个轴的坐标
$pos-&gt;level
Level
返回Position所在的世界
$pos-&gt;getSide($side)
Position
同Vector3的getSide
$pos-&gt;distance(同Vector3)
double
同Vector3的distance
这样子表示位置是不是方便多了呢~


&gt; Level对象
- 源文件位置:/src/world/Level.php
- 方法
如果$lev是一个level对象,那么你可以——
======================== 坑线 ==========================
第三讲完
======================== 坑线 ==========================
4.PM API解析【已更API11】


第四讲完

Snake1999
本帖最后由 Snake1999 于 2014-1-30 10:38 编辑

本楼备用!!

我勒个去234567
不是金锭我不收

hsk001ufo
大哥说了多少遍了=。=能都发一楼么

hsk001ufo
话说我怎么感觉发错地方了=。=

592764254
.           

hsk001ufo
有更新了啊=。=

hsk001ufo
在【其次,你得会玩PHP】里面有及部分引用PM内部代码的部分路径不对,如PocketMine-MP/src/ConsoleAPI这个应该是PocketMine-MP/src/API/ConsoleAPI.php,希望能改正一下

ljyloo
好,涨姿势了{:10_505:}

uuuu1
怎么监视文件...
还有,php怎么读取txt内容。。

Snake1999
uuuu1 发表于 2014-2-7 14:31
怎么监视文件...
还有,php怎么读取txt内容。。

这个你去看W3SCHOOL的教程,我这里限于篇幅不多讲

西瓜太郎
赞               

zmdd
那啥。。为什么我在上面写了中文回复是乱码?

梦の小黑
lz你好牛!智商好高!赞一个!然后·······我的脑子炸了(我一小学生懂个毛函数啊啊······)

XM君君
二菌快更啊= =还有我编写了一端插件,档读取木牌文本的时候pm的使用内存暴增,然后崩溃了。。。肿么破

asdfff啊
楼主好人 我要开始学习了

q1272686154
哎呀受教了,嘿嘿,顶起→_→

850639946
太吊了,我还以为和PC的bukkit插件差不多,看了别人的源码-_-||

martinsmith
PHP有点像C语言和Java。。。

圣_痕
话说,我怎么越到后面越听不懂

nmh1234
楼主加qq1766355378 我需要您的帮助 多少钱都可以接受的, 我最近开服

nmh1234
楼主在? 给你钱来我的服务器定制插件吧。都是同类人 我只不过是pc的1.7.9API弄罢了被pm限制了 求楼主加qq1766355378

CLOST
毛线,e才简单QwQ

DHRSW
这。。。。。。看完我还是不会啊

by4399荣耀君
路过帮顶

孤独秋叶
教程完全比java还难

2503180246
可以的~!支持一下楼主 楼主加油好好干=-=
火起来~~~

究竟2000
顶顶顶顶顶顶顶顶顶顶顶顶顶顶

wyywang
蟹蟹楼主!!

mohist
谢谢楼主分享,教程很棒很有用

第一页 上一页 下一页 最后一页