- 博客(51)
- 收藏
- 关注
原创 2025/11/13 -- 组队系统
总的后处理入口中添加组队系统的后处理,如果角色目前的队伍信息的更新时间戳要小于Team的更新时间戳,就代表不是最新的信息,触发组队系统的后处理,因为这里是在Character里面添加的字段,所以每个队伍成员都会有该字段,这样就可以保证一个人加入队伍的时候,其他的队伍成员都会通过后处理更新一遍。这里ListView的元素是固定5个的,所以在start的时候,给其添加上去。这里的ListView的刷新逻辑和以往的有区别,以往都是直接实例化出来的,这里是控制显隐的个数,显示出来的给其设置值。
2025-11-14 19:55:58
849
原创 2025/11/12 -- 好友系统
协议的话,有删除协议,和添加好友协议,不过有一个地方和之前不同的是,在添加好友时客户端向服务端发送请求,服务端就收到后,向客户端发送请求,客户端接收到请求返回响应,服务端再给这个响应返回到刚开始发送请求的人手中,也就是服务端和客户端都涉及到请求的发送以及响应。当接收到客户端返回的响应时,判断是否成功,如果成功添加但是请求着已下线,给B发送A已经下线的通知。当找到好友的id时,看是否已经属于好友,如果不是好友,从会话管理器里面拿到目标的会话,这里用会话的目的是因为用到了一个后处理的概念,减少发包量。
2025-11-13 15:28:47
617
原创 2025/11/10 -- 刷怪系统
定义了一些字段,define是刷怪规则,unspawnTime是上次怪消失的事件,spawned表示是否刷过怪,spawnPoint就是客户端通过扩展工具类生成的那些信息。这里也和之前的传送点的构建差不多,就是遍历一下所有的地图,找到带SpawnPoint类的点,之后获取该点的位置以及方向,给这些信息保存下来。该类实际上就是做的一个可视化的工作,和之前的传送差不多,画了一个外框,一个红色的箭头代表刷怪的方向。这次涉及到两个表,一个是刷怪规则,一个刷怪点,刷怪点是由客户端的扩展工具类生成的。
2025-11-11 13:22:12
338
原创 2025/11/9 -- 任务系统
这里的NQuestInfo主要是4个字段,一个是任务的id,是任务表里面的任务id,一个guid来作为唯一标识,status来表示任务当前的状态,targets来表示任务要达到的目标。这里的关闭方法分为Yes和No两种结果,Yes里面看当前的任务是否已经接过,没接过发送接任务的请求,接过就是提交的状态。对于服务器里面已经接过的任务,同步到客户端,再遍历一下当前可接的任务。维护的选中元素LIstViewItem,以及通过属性来处理选择新的元素时会触发的反应,这里会调用最开始写的事件里面的方法。
2025-11-10 15:22:42
771
原创 2025/11/7 -- 装备系统
2.4 该Manager是单例类,里面包含了一个装备的物品的方法以更新装备的方法,这里的 装备方法首先是需要效验是否已经装备过,之后更新装备的二进制数组,保存到DB里面。如果当前的槽位还有装备在用一个二次确定来提示是否替换,如果替换就用ower里面的穿装备的方法,那个方法应该是调用Manager里面的接口,Manager再调用Service里面的接口来对客户端进行请求。可以看到装备字段也是二进制字符串,背包里面采用的也是二进制字符串,不过背包里面的长度是不固定的,装备的槽位是固定好的,最大槽位是7。
2025-11-08 07:47:38
545
原创 2025/11/4 -- 商店系统
3.3 UIShopItem里面主要是设置了价格,个数,以及物品图标、选择高亮,这里的选择高亮是通过ISelectHandler接口来实现的,该接口是Unity自带的,这样就不用给Itemrender绑定一个Button了,选择属性时设置高亮显示。该接口会将状态管理器里面的状态列表都加到总协议里面,所以当返回协议的时候,在过程中添加的所有状态信息都会自动的发送到客户端,这样购买商店物品的时候,就不用发送多个协议了,只需要添加状态,在返回的时候,会自动同步金钱物品之类的信息。该字段是关于状态系统相关的。
2025-11-05 20:06:08
776
原创 2025/11/3 --- 背包系统
初始化函数里面会先对数据进行保存,然后判断是否是第一次登录,因为背包里的数据是从ItemManager来获取到的,如果是第一次登录,这时info肯定是为空的。BagItem是用结构体进行实现的,因为如果采用类的话,类是引用类型,后面想交换两个背包格子的话,结构体的值类型相较于类的引用类型,逻辑会简单些。2.1 DB里面的背包存储的结构,一个ID,一个字节数组表示Items,一个解锁的背包格子数,其与角色相互关联,一对一。start方法里面先获取到所有的格子,每个格子都是以一个Image为背景图的。
2025-11-04 14:55:39
667
原创 2025/11/2复习--道具系统
因为服务端的ItemManager是一个角色对应一个ItemManager,所以这里不使用单例类,这里用字典来维护Item数据,这里存储的Item是Model层里面的Item, 构造函数里面会知道该ItemManager的拥有者是谁,并且会遍历数据库里面的数据,在该Items里面添加一下。这里是添加方法,如果已经拥有直接添加,如果没有的话,需要先创建一个数据库里面的TCharacterItem,设置一下该结构里面的一些字段,我们Model层里面的数据是通过DB层里面的数据转换来的。
2025-11-03 12:06:07
959
原创 2025/11/1 复习--NPC系统
Manager层主要是进行一些逻辑上的处理,Control负责一些实体相关的操作。对交互方法的重载,可以用npc的Id以及npc的定义来调用交互操作,交互操作首先判断Npc目前是否有任务,如果有任务的话先对人物进行相关的处理,再进行功能上的交互。该脚本里面主要是通过携程来模拟一下NPC动作的随机性,以及NPC的高亮处理,以及NPC点击时的转向动画,并且调用NPCManager里面的事件方法。在start函数里面先获取到NPC原始的颜色,以及NPC的动画组件,NPC的表数据,开始一下NPC的动作携程。
2025-11-02 13:53:51
836
原创 2025/11/1 复习--UI框架设计
UI框架的设计,对于UI框架来说,UI可以分主UI、UIManager、UITipsManager、UIMessageBox,也就是UIMain, 比如说左上角的角色信息,右上角的小地图都是主UI,也就是哪些会一直显示的。UIManager主要是用于管理哪些需要点击,主动关闭的UI,当不需要点开的时候,UI资源不会去加载,当关闭的时候,释放UI资源,而UITipsMananger主要就是用于处理那些提示之类的的UI,UIMessageBox就是那些二次确认面板上面的UI。在构造函数里去注册UI相关的信息。
2025-11-02 13:20:57
341
原创 2025/10/29 复习
之后遍历每一个地图,对于每一个地图,先打开地图场景,搜索场景内所有的挂载TeleporterObject的对象,获取其位置以及方向信息。主要是给之前做的功能里面的一些bug进行了修复以及做了一下地图的传送功能,涉及到对于触发器的使用,以及扩展一下工具类。主要是先看一下传入点以及目标点是否合法,如果合法的话,就先调用离开地图的方法,再将生成出来的位置以及方向更新到entity里面,最后调用角色进入地图的方法。写了一个只在编辑器下起效的方法,主要是用来看传入点的方向,角色传入的时候,角色方向和这个一致。
2025-10-30 13:21:13
286
原创 2025/10/28 复习
今天主要是继续完成状态同步,主要流程是在PlayerInputController接收到玩家输入的时候,会去调用SendEntityEvent方法,该方法会调用MapService里面的同步请求,给服务端发送自己的NEntity信息,服务端接到后会给服务端自己的NEntity信息同步更新一下,再广播给其他所以人。同步成最新的数据后,会调用实现的观察者模式里面的方法,来改变角色的动画状态机相关的设置。服务器接收到信息后会调用地图管理器里面存储的Map里面的同步实体的方法。就是设置角色的动画状态机。
2025-10-29 13:30:24
320
原创 2025/10/27 复习
创建角色的时候会直接调用世界元素管理器里面的添加血条方法给角色头顶添加信息条,删除角色血条是通过EntityManager里面实现的观察者模式来通知EntityControl来删除角色头顶的信息条。这里的请求和之前的差不多,这里的回调,当接收到服务器的响应后,给user里面的当前存储的当前角色给清空,并且给service里面的当前地图id给设置为0.这里面是先检查该实体id有没有注册相对应的方法,如果有的话就先调用该注册的方法,再移除。调用该地图的角色离开方法,广播给该地图里面的所有角色通知该角色离开了。
2025-10-28 12:25:03
257
原创 2025.10.26 复习
添加信息条就是实例化一下,设置位置就好了,移除是先销毁相对应的信息条,在从字典里面删除,这里添加信息条是统一添加到该管理器下面,这样如果给该管理器销毁掉,所有的信息条都会相对应销毁掉。对于正常的UI主页面的右上角的玩家信息,正常情况下是有蓝条,血条,等级,名字之类的信息。通过update来实时的检测信息条,如果说该玩家信息不为空时就可以更新该信息条相对应的名字,以及等级之类的信息,暂时头像还没有。1. 今天主要是做一下主UI上面的部分功能,比如玩家的姓名条, 左上角的玩家信息,以及右上角的小地图。
2025-10-27 04:00:44
361
原创 2025.10.24 复习
当实例化角色实体后,会赋值一些数据,因为一个角色GameObject上面会有一个PlayerInputController用来处理玩家的输入,还有一个EntityController。这样在服务器给客户端同步数据的时候,就只需要同步一部分的数据,Entity是Character的父类。在添加角色实体的时候,还会通过EntityManager来添加实体管理器。角色管理器添加实体的时候还会调用事件来创建对应的GameObject。来更新该GameObject相关的位置方向,并且存储一下相关的信息。
2025-10-25 04:42:11
328
原创 2025.10.23 复习
之后封装消息给客户端发回去,实际上目前给客户端发回的消息里面并没有什么需要处理的东西,后面是在地图管理器里面,在相对应的地图上调用角色进入的方法。2. 学习mmo的课,目前主要是在进入主城的部分,进入主城里面设计到的东西稍微有点多,比如说角色管理器,地图管理器之类的。调用地图管理器里面的角色进入方法,会先给该地图里面的所有角色通知一下该角色的进入。进入地图的接口里面主要是根据地图id来知道表里面配的地图名字,通过该地图名字来切换地图。角色管理器里面,目前是用字典存储角色的,一个Id对应一个角色。
2025-10-24 04:20:39
308
原创 2025.10.22 复习
请求都是在server层来写,所以需要在UserServer添加发送请求以及接收服务器响应的回调,这个和我之前做cocos时,module类里面处理发送接收协议很像。当在数据库里面添加一个新角色时,会在session里面对应的添加上该角色,省的要再到服务器里面再找一下。给session存储到的角色信息包装到sender里面。这里主要就是包装一下发送的信息,以及判断一下网络的连接状态,这个判断网络连接状态在之前的登录界面也同样有。接收服务器响应的方法主要就是在Model层里面的User里面给角色列表存一下。
2025-10-23 03:20:42
248
原创 2025/10/21 复习
初始化里面,主要就是做的遍历从服务器传来的角色列表的数据,实例化出来,然后默认是指第一个,以及添加点击事件。上面的是用来根据text的高度计算content的高度的。刚进入的时候是在选择面板,点击底部的按钮会进入创建面板。用着三个方法来处理鼠标的点击,点击中,以及点击完毕三种状态,当开始点击是会记录当前的鼠标的屏幕x方向的位置,并且给当前设置为可旋转状态。在scrollView的content里面加一个text,来实现上下滑动文本的效果的。对应着三个按钮,就是绑定的选择职业方法,该方法会切换左边的模型。
2025-10-22 03:26:28
271
原创 205.10.19 复习
2.3 碰撞器是触发器的载体,而触发器只是碰撞器的一个属性,当给IsTrigger设置为false的时候,碰撞器会根据物理引擎产生碰撞效果,调用的是OnCollisionEnter/Stay/Exit,而当设置为false的时候,碰撞器会被物理引擎忽略,不会产生碰撞效果,调用OnTriggerEnter/Stay/Exit。2.8 ref关键字是双向的,但是传入的时候需要被初始化,即使传入的是一个值类型,在该方法里面对其进行修改,值类型也会自动发生改变。List,底层泛型数组,数据类型安全,动态扩容。
2025-10-20 03:51:05
817
原创 2025.10.18 复习
1. MVC指的是模型-视图-控制器的缩写.Model里面通常是用于处理数据的相关逻辑,View是用于数据的显示,Controller用于协调Model和View,接收用户的输入,让Model处理数据,让View调整显示。10. JIT和AOT的区别,JIT是运行时编译,AOT是预编译,所以JIT的启动时间长,运行快,AOT是启动快,运行也快,但是AOT的内存大小会比JIT要大。字典的实现原理主要是哈希算法,哈希桶算法,拉链法,哈希算法算出一个对应的hashCode,哈希桶算是计算其存储的位置。
2025-10-19 02:52:45
437
原创 2025.10.18 复习
ArrayList是在C#泛型还没有出现之前主要使用的集合类型,其实现与IList接口,因为其会将所有的元素都看成是Object,所以会涉及到拆箱和装箱,在实例化的时候需要声明出实际的类型。Assembly用于描述程序集,Type用于描述类的类型, FieldInfo描述了类的字段, PropertyInfo描述类的属性,MethodInfo描述类的方法。interface是完全抽象,抽象类是部分抽象,里面不仅可以包含一些没有具体实现的方法,同样也可以包含一些有具体实现的方法,所以是部分抽象。
2025-10-18 03:50:15
356
原创 2025.10.16复习
效验完后,在UserService里面写发送请求的方法,以及接收到请求的回调。这里先是构建请求消息,当正在连接时,就直接通过NetClient发送消息,如果没有连接就先给消息存储起来,调用连接服务器的方法。并且在UserService里面给接收消息的回调注册一下,以及创建一个Action用来处理后续的UI界面的处理。这里面暂时注册了这一种方法,如果服务器发过来的结果是成功,就加载角色选择场景,否则就弹出失败的提示框。当接收到消息后就会调用创建的action来调用里面注册的方法。
2025-10-17 04:01:40
339
原创 2025.10.15 复习
2. 做MMO的用户登录界面,主要就是Loading场景的制作。刚开始的提示动画,之后加载进度条,加载完后进入登录界面,登录界面有注册和登录按钮。比如开始start携程时,会先设置提示动画为true,等待两秒后给加载进度条的页面设置为true。写的代码越少性能越高。因为有一些输入框,所以做了一些对于输入框的判断。并且用了一个相对通用的UI预支UIMessageBox来解决提示框的问题。1. 给昨天的二叉搜索树的题目做完通过,实际上调试还是调试挺久的。后面去看了一下别人写的代码发现可以写的更简洁点。
2025-10-16 03:37:34
133
原创 2025.10.14 复习
课中还提到了屏幕适配的相关方案,比如说在有的界面里的背景图,如果没考虑适配问题的话,可能会出现上下空出来一大块。提到的一个方案是,在做背景图的时候,就考虑到这种情况,给背景图的高度做的高一些。实际上我在做cocos业务的时候,对于那些全屏的背景图,我看资源都是做的高度比较长的。不过在拼UI的时候要注意图片资源的像素尺寸,正常在做UI界面的时候,都是根据这个来调大小的。当知道根节点后,以及左右子树的特点,就可以判断出右子树的序列区间以及左子树的序列区间。1.其左节点树上所有的节点的值都小于该节点的值。
2025-10-15 02:50:21
215
原创 2025.10.14复习
服务端主要是定义一个消息分发器、消息分配器、数据包处理器、NetService,UserService等,UserService主要是订阅请求以及编写处理请求的方法,在处理完后会给处理完的数据发回给客户端。第二题是经典的逆序对问题,正常逆序对问题都是通过归并排序来解决的,归并排序会先给数组无线细分到最小块,之后从最小块开始给其排成有序的,再两两合并,在归并排序的过程中通常是前面一部分和后面一部分都是分别有序的,前面的i号元素大于后面的j号元素时,i到middle之间的所有元素都是逆序对。
2025-10-14 02:47:23
247
原创 2025.10.12日复习
值类型正常是存储在栈中,但是也可能存储在堆中,比如说实现了一个类,类的引用类型,存储在堆中,类里面有一些值类型的变量,会跟着存储在对重。同样引用类型正常存储在堆中,但是如果定义了一个引用类型的变量,这个变量的地址会存储在栈中,因为该变量是临时变量,临时变量存储在栈中。值类型会自动回收,存取速度快,引用类型靠GC自动回收,存储速度慢,值类型存储的是数据的本身,引用类型存储的是数据的地址。暂时刷的还是简单的,循序渐进,里面用到了快速幂,二分,记忆化存储,还有栈之类的简单数据结构。比如重载,重写,接口多态。
2025-10-13 02:37:25
286
原创 背水一战真是好计谋
各位,我在公司工作三个月,凭良心说,第一个月提升最大,第二个月提升稍微变慢,第三个月,提升已经非常慢了,并且安排给我做的都是做过的业务。所以我决定明天辞职,边学边找Unity,我觉得该公司现在对我来说是枷锁,已经成为我实现我梦想的绊脚石。这次背水一战,可以绝对肯定的说,接下来的几个月绝对会比我高考、大学、考研、甚至这几个月的努力的多。当身后已无路时,才能真的激发一个人的潜力,该断则断。这次我绝对会每天晚上准时发布我今天学了什么,干了什么。岂不闻,天无绝人之路,各位道友,祝我好运!
2025-10-11 22:30:59
170
原创 从0搭建XLua热更新框架----对象池
可以看到LoginUI显示进入到子节点UI上面被隐藏掉,现在是被UI对象池回收了,UI对象池是10秒对池子里面的对象进行一次清理,通过日志可以看出,GameObjectPool对象池释放对象在42分10秒,这时池子里的对象,loginui的引用数为0,button图片的引用数为1,是因为TestUI还没有关闭,再过10秒后,对bundle对象池里面的对象进行卸载。当没有创建过该对象池时,创建对象池,设置父节点,获取相对应的对象池的脚本,进行对象池的初始化,将对象池添加到字典里,方便后续的统一管理。
2025-10-05 22:52:20
660
原创 从0搭建XLua热更新框架------事件管理器
这里的事件管理器实际上就是实现了一个简单的观察者模式,当需要加载Lua脚本的时候抛出一个事件,就会执行相对应的逻辑。
2025-10-05 00:35:32
167
原创 从0搭建XLua热更新框架------音乐管理器
这些方法的绑定实际上都是在调用SoundManager里面相关封装好的接口,总体来说声音管理器写起来还是比较简单的。如果音量过小或者已经播放该音乐就不再播放了,否则异步加载音乐的资源。基本上都是一个按钮对应一个SoundManager里面的一个功能。先寻找到后面需要绑定方法的相关组件。
2025-10-01 20:45:20
221
原创 从0搭建XLua热更框架------实体管理器与场景管理器
SceneLogic主要有进入场景,退出场景,激活以及隐藏相关的方法.可以看到成功叠加了两个场景以及加载出来了player实体.
2025-09-08 01:48:19
462
原创 从0搭建XLua热更新框架------LuaBehaviour,UILogic,UI层级管理
2. 因为要绑定Lua脚本,但是在Awake的时候还拿不到Lua脚本的名字,所以定义了一个Init方法,将Lua的名字作为参数传进去进行Lua脚本的绑定。但是有的时候会涉及到跑马灯一些的设计,或者一个页面会出现十几个UI弹版的情况,因此需要对弹版写一个Manager来进行管理,会给弹版分组,对应的弹版放到对应的组里面,这样就比较容易管理UI层级了。3. 在C#里面的Update方法时,会调用Lua里面定义的Update方法,在OnDestroy里面调用Lua里面的OnDestroy。
2025-08-31 21:08:11
496
原创 从0搭建XLua热更新框架------Lua管理器
非编辑器模式下通过回调来确保在所有Lua脚本加载完成后才能够执行Lua脚本,因为加载Lua脚本时是异步的,如果直接按顺序在下一行写执行脚本,可能会发生脚本还没加载好就已经执行了。这里通过定义的方法去执行名字叫Main的Lua脚本,实际上该方法里面只有一行require,当执行这一句完成后会自动调用之前注册的加载里里面的方法获取Lua脚本的字节数组格式。Lua脚本的处理主要分为两种,一种是编辑器模式下加载Lua脚本,以及非编辑器模式下加载脚本。可以看到Lua脚本正确执行。2. 加载Lua脚本。
2025-08-26 01:29:55
212
原创 设计模式----命令模式
不过也可以发现,有的游戏里面是支持改按键的,你可以给跳跃键改为W,也可以改为K,如果说我们监听输入,用if(按了空格)那么就跳跃这样来做,想要实现改键那种操作是比较麻烦的。这时候命令模式就可以发挥作用了。接下来实现该接口的命令都将拥有一个执行操作和一个撤销操作,或者说实现该接口的那些类肯定是一个命令对象,一个命令对象一定有执行和撤销操作。命令模式还是挺有用处的,如果你想实现一个回溯的效果,或许当你按下技能时,将玩家人物以及场景里面可交互的东西,以及怪物,这些对象都将通过撤销操作可能统一回溯到10秒之前。
2025-08-24 23:59:14
384
原创 从0搭建XLua热更新框架------热更新
1.3 这里的释放包内资源主要是因为streamAssets是一个只读的目录,所以后续更新不能写入,为了方便就额外准备了一个可读写地方,将只读目录里面的东西拷贝到可读写目录里,释放包内资源也就是将资源释放到可读写目录里。分包模式:将少量或者不将更新资源放在安装包里面,所以下载的安装包会比较小,但是第一次更新的资源量会比较大,比如说原神就是分包,下载的安装包可能只有几百M,但是下载完更新需要几十G的内存。可以看到资源是从服务器的地址下载进来的,下载完后只读目录依旧为空,但是本地的可读写目录里面会出现资源。
2025-08-24 23:07:41
453
原创 从0搭建热更新框架复习--资源加载,资源路径规划,编辑器模式加载资源
其里面包含了资源名字,bundle名字,以及依赖资源。所有资源加载需要先解析文件信息列表,递归式的去异步加载其依赖资源。最后加载本体的bundle资源,完成资源的加载。可以看到日志里面出现了正在运用异步来加载UI资源,说明我们解析了版本文件,递归加载依赖,异步加载bundle。这也就是上面start里面的加载UI资源函数,通过层层封装,实现一句代码加载UI资源,在调用该函数时,并不需要去关系里面的实现细节。直接获取现有的资源,不用去重复构建bundle加载,方便开发。3. 编辑器模式加载资源。
2025-08-18 01:45:35
438
原创 从0搭建XLua热更新框架复习-------编写bundle构建工具
热更新最重要的就是对于bundle的处理,热更主要更新的就是bundle,bundle的处理中又可以分为构建bundle、加载bundle、更新bundle。如上思维导图,本框架资源目录的划分主要以是否需要打bundle为区分点进行划分,需要打bundle的主要有Lua脚本以及各种游戏资源,对于不需要打bundle的有C#脚本以及XLua插件。该部分先是递归给输出文件夹删去,再创建一个新的来保证输出的目录存在,之后调用unity的接口来构建bundle,之后保存信息列表以及刷新,让信息列表显示出来。
2025-08-11 00:31:24
553
原创 Lua复习--------闭包
可以看到每次执行该函数,i值并没有被重置,因为该函数的返回值是一个函数,该函数称为闭包,也就是形成了一个独立的环境,所以后面每次执行该函数时,都只会从return的地方开始,给局部变量设立初值的那一步不会执行到。因为Lua定义的函数中,传入的参数以及返回值都可以是一个函数,由此出现了闭包。上面函数特别的地方在于其返回的是一个匿名函数,给局部变量i加了一次,之后返回。可以看到这两个计数器中局部变量i所处的环境都是相互独立的,这就是闭包的概念。
2025-08-04 02:10:27
221
原创 Lua复习--------实现继承以及多态
实际上继承就是用__index来实现,元表最后设置的关系大概是:obj的元表是MagicMonster, MagicMonster的元表是CMonster,像是一个链式的关系结构。如果不设定MagicMonster的show方法,会自动寻找到元表里的show方法,设定的话就用自己的。1. 定义一个MagicMonster类。2. 继承自CMonster。3. 重写show方法。
2025-08-04 01:06:30
175
原创 Lua复习------------用Lua模拟面向对象
上面代码中使用Lua内置的函数setmetatable来为CMonster设置初始化函数,当我将CMonster当做函数名字使用时,就会调用__call所指向的函数,该函数里先构建了一张空表,之后设置了一些属性,最后将该表的元表设置为了CMonster,也就是模拟的这个类。setmetatable函数的第二个参数是一张表,当我将obj的元表设置为一张表时,就会去寻找__index这个下标所指向的位置,当我访问obj不存在的下标时,会优先去该指向的位置处寻找。上面代码中,定义了该类的一些方法。
2025-08-03 17:02:56
206
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅