Jump to content

Recommended Posts

Posted (edited)

So I was bored again...

 

L2jRest is a RESTful API for L2j

 

It is created for latest aCis but you should be able to adapt it is you wanna use it for other projects.

 

L2jRest is an open source project licensed under MIT. You can find the source here: https://github.com/Elfocrash/L2jRest

 

Why use it?

  • You can use the data of your server in your website
  • You can create an account control panel with it
  • You can expose data to your community to allow them to make third party apps

 

Technical stuff

  • It is written in Kotlin.
  • It is using the Ktor framework. Ktor is using coroutines to achieve asynchronous request handling. It is very efficient and very fast.
  • It is using Netty as the underlying server.
  • It is written in a CQRS manner with query handlers for get endpoints and command handlers for post, put, patch, delete etc.
  • It is using Koin as the IoC framework.

 

How to setup

  • Download and install Intellij IDEA
  • Git clone the project and open it with Intellij
  • Run the build Gradle task. It is configured to create a fat jar with all the dependencies included
  • Paste the jar in your project, add it in your classpath and add the following line at the bottom of your Gameserver.java: L2jRestApi.INSTANCE.start();
  • Running the gameserver will also run the api. It is running under port 6969

 

How to extend it

  • All you need to do if you wanna add more endpoints is to add a new handler and then add the Get, Post etc annotation depending on what endpoint you want this to be.

 

Current endpoints:

http://localhost:6969/api/players

http://localhost:6969/api/players/{id}

 

A couple of endpoint examples

 

Endpoints: http://localhost:6969/api/players

Response:

{
    players: [
		{
			"id": 268480927,
            "name": "Test",
            "level": 1,
            "isOnline": false,
            "pvpKills": 0,
            "pkKills": 0,
            "isNobless": false
		},
		{
			"id": 268480924,
            "name": "Test2",
            "level": 1,
            "isOnline": true,
            "pvpKills": 0,
            "pkKills": 0,
            "isNobless": false
		}
	]
}

 

Endpoint: http://localhost:6969/api/players/268480927

Response:

{
    "id": 268480927,
    "name": "Test",
    "level": 1,
    "isOnline": false,
    "pvpKills": 0,
    "pkKills": 0,
    "isNobless": false
}

 

Currently it just supports two endpoints and no authentication. I am planning to add more endpoints and ApiKey based auth tomorrow.

 

If you can't be arsed to build the project yourself but you wanna take a look anyway you can download the jar here

 

Edited by Elfocrash
  • Like 3
  • Upvote 2
Posted (edited)

Thank you a lot for that. That's what I was searching. I have one question

 

L2DatabaseFactory.getInstance().connection.use { con ->
                val statement = con.prepareStatement(SqlQueries.GetAllPlayers)
                val resultSet = statement.executeQuery()

                var player: PlayerResponse? = null
                if (resultSet.next())
                {
                    val id = resultSet.getInt("obj_Id")
                    val name = resultSet.getString("char_name")
                    val level = resultSet.getInt("level")
                    val isOnline = resultSet.getInt("online") == 1
                    val pvpKills = resultSet.getInt("pvpkills")
                    val pkKills = resultSet.getInt("pkkills")
                    val isNobless = resultSet.getInt("nobless") == 1
                    player = PlayerResponse(id, name, level, isOnline, pvpKills, pkKills, isNobless)

 

 

Wouldn't be better to take these infos from the player object itself for example?(if player is online) So it would be real time , because database values are not always real time on L2J due to delay of updating. This specific feature may isn't the best example to what I mean actually but propably you got the point.

 

Also practically could we extend this to create token authentication and the ACP(or whatever) to just be  a cool SPA with front end framework(react,vue,etc)? 

Edited by Lioy
Posted (edited)
52 minutes ago, Lioy said:

Thank you a lot for that. That's what I was searching. I have one question

 


L2DatabaseFactory.getInstance().connection.use { con ->
                val statement = con.prepareStatement(SqlQueries.GetAllPlayers)
                val resultSet = statement.executeQuery()

                var player: PlayerResponse? = null
                if (resultSet.next())
                {
                    val id = resultSet.getInt("obj_Id")
                    val name = resultSet.getString("char_name")
                    val level = resultSet.getInt("level")
                    val isOnline = resultSet.getInt("online") == 1
                    val pvpKills = resultSet.getInt("pvpkills")
                    val pkKills = resultSet.getInt("pkkills")
                    val isNobless = resultSet.getInt("nobless") == 1
                    player = PlayerResponse(id, name, level, isOnline, pvpKills, pkKills, isNobless)

 

 

Wouldn't be better to take these infos from the player object itself for example?(if player is online) So it would be real time , because database values are not always real time on L2J due to delay of updating. This specific feature may isn't the best example to what I mean actually but propably you got the point.

 

Also practically could we extend this to create token authentication and the ACP(or whatever) to be just a cool SPA with front end framework(react,vue,etc)? 

The problem is that the offline players are not loaded in memory so the dB call is inevitable. Everything that is loaded in the in memory cache will be served by the cache when possible. If the endpoint was get online players only then yeah it would be coming from the World class.

 

Auth is very simple to add. It’s basically a simple middleware.

 

originally when I started this it was to actually make a new ACP but it’s too much work and I don’t really wanna do front end anymore so I just created the api. If people wanna make their front end that’s fine by me. 

 

EDIT: Oh I now see what you mean, I read it wrong the first time. Yes it would make sense to get that info from the cache if the player is online. I will add that.

Edited by Elfocrash
Posted (edited)

PlayerInfoTable retains some informations (cause player names are needed for stuff like friendlist), you probably can add missing ones, but it means you have to update it in same time otherwise you will refer to wrong values.

 

That whole class probably can be enhanced or used differently (PlayerInfo being maybe part of Player, once player is loaded ? Like loading an id card, which avoid duplicates variables or even caring about refreshing 2 places).

 

If such stuff is edited, that would simplify the developement of a lot of customs.

Edited by Tryskell
Posted
1 minute ago, Tryskell said:

PlayerInfoTable retains some informations (cause player names are needed for stuff like friendlist), you probably can add missing ones, but it means you have to update it in same time otherwise you will refer to wrong values.

 

That whole class probably can be enhanced or used differently (maybe part of Player, once player is loaded ? Like loading an id card, which avoid duplicates variables).

The problem with having multiple points of truth is that you don't have a single one you can fully trust, that's why i trust nothing but the database and the World class. I also don't wanna touch the core at all so if I do any caching it will happen in the API level.

Posted (edited)
3 minutes ago, Elfocrash said:

The problem with having multiple points of truth is that you don't have a single one you can fully trust, that's why i trust nothing but the database and the World class. I also don't wanna touch the core at all so if I do any caching it will happen in the API level.

 

I edited my answer :), and the idea is to only have one point of entry, which would be PlayerInfo linked to a Player, like they are actually linked to an AccessLevel, a Appearance, etc. SO drop inner variables of Player, and use only PlayerInfo to refresh those values.

 

Np.

Edited by Tryskell
Posted

Changed the way handlers are registered. Now they will be scanned automatically during server startup and they will be registered based on their annotation. This means that all you have to do to add a new endpoint is to create a request handler and add the Get, Post etc annotation.

Posted

Added api key based authentication as requested. Requests will only be served when the x-api-key header is set with the configured value.

Posted
On 7/24/2019 at 1:25 AM, Elfocrash said:

Added api key based authentication as requested. Requests will only be served when the x-api-key header is set with the configured value.

 

Hey Elfo , what's the logic on this authentication? If credentials are correct then send the API key to the client? And all authorization is possible via this simple unique API key?

Just asking, because never worked with that kind of auth. (I know JWT for example , maybe I confused things somewhere)

Posted
2 minutes ago, Lioy said:

 

Hey Elfo , what's the logic on this authentication? If credentials are correct then send the API key to the client? And all authorization is possible via this simple unique API key?

Just asking, because never worked with that kind of auth. (I know JWT for example , maybe I confused things somewhere)

It's using ApiKey auth. It's not client facing auth where you get credentials and return a key. You can do that with Basic auth but you have to enable it.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



  • Posts

    • sell adena l2rebon signature x1 - 1kk = 1.4 dollars l2reborn x10 - 500kk = 7 dollars E-Global x Lu4 - 1kk = 4 dollars DISCORD - GODDARDSHOP TELEGRAM - MMOPROMO
    • > X Buys 10-50k fake discord member (online/offline), so it looks "active", yes today you can also buy fake chatters. > X Advertises his discord server in forums etc trying to sell @everyone posts > Buyer doesn't understand why the purchase wasn't getting any new players Clownery. So all in one, imho buying this kind of stuff is not worth at all, despite the low proportion of real people in these servers got discord muted either way, especially becuse you get pinged and informed 42930423904 a day about some new lineage 2 server that was made just for money and sucks too.
    • If your main goal is to reach active L2 players in Lithuania, Discord is honestly the best bet right now. Most of the old forum traffic died down, but Discord groups stay busy with trading, clan recruiting, and events. Just make sure you check how engaged the channel actually is before spending money, some look big but aren’t that active.  
    • I’ve tried a few ad providers from here before, some were hit or miss. Best thing is to ask for recent results or proof before you spend anything, helps avoid wasting money.  
    • SOCNET BOOST - 是所有现有 SMM 面板中的新解决方案. 我们的面板提供超过 6100 种社交网络推广服务,包括 Instagram、TikTok、Telegram、VKontakte、Reddit、YouTube、Twitter (X.com)、Snapchat、Spotify 等众多服务 + 提升您网站在搜索结果中的流量。 我们的服务与高质量、可靠的服务提供商合作,为您的社交网络账号、帖子、视频、文章等提供推广,以及提升网站在搜索引擎中的排名,同时提供通过推荐赚取额外收入的方式。 可用支付方式: 通过 PayPal、任意加密货币 (+Binance Pay)、Telegram Stars、Cash App 或任何银行卡支付。 ⭐ 我们的社交媒体推广 SMM 面板 ⭐ SOCNET.PRO ⭐ 我们的在线商店 ⭐ SOCNET.STORE ⭐ Telegram 商店 ⭐ SOCNET.SHOP   新闻资源 Telegram 频道: https://t.me/socnet_news 联系方式与支持 Telegram 支持: https://t.me/socnet_support Discord 支持: socnet_support Discord 服务器: https://discord.gg/y9AStFFsrh WhatsApp 支持: https://wa.me/79051904467 WhatsApp 频道: https://whatsapp.com/channel/0029Vau0CMX002TGkD4uHa2n 电子邮件支持: solomonbog@socnet.store 我们已经运营了很长时间,积累了大量关于我们工作的评价! 我们的网站展示了大量积极和真实的用户评价!   ㅤㅤINSTAGRAM 服务 Instagram - 粉丝 ~ 真实 ~ 30天补充 ~ 20k-100k/天 | 每 1000 起价 $5.324 Instagram - 点赞 ~ 300k ~ 100k/天 ~ 亚洲/俄罗斯 | 每 1000 起价 $0.051 Instagram - 浏览量 (所有视频) ~ 500k-1M/天 ~ 即时 | 每 1000 起价 $0.003 Instagram - 评论 ~ 自定义 ~ 1M ~ 30天补充 ~ 1k/天 ~ 亚洲/土耳其 | 每 1000 起价 $4.875 Instagram - Instagram - 故事浏览 ~ 所有故事 ~ 150k ~ 30天补充 ~ 5k/天 | 每 1000 起价 $0.148 Instagram - 提及 ~ 自定义列表 ~ 100k/天 ~ 0-12小时 | 每 1000 起价 $2.763 ㅤㅤTELEGRAM 服务 Telegram - 成员 ~ 最大 100k ~ 100-1k/天 ~ 0-1小时 | 每 1000 起价 $0.604 Telegram - 高级成员 + 浏览量:从搜索 l 7天高级 l 7天不掉 | 每 1000 起价 $7.216 Telegram Bot 启动 - 即时 - 速度: 10k/天 - 不补充 | 每 1000 起价 $0.297 Telegram - 频道/群成员 ~ 美国 ~ 最大 70k ~ 10天补充 ~ 5k/天 ~ 即时 | 每 1000 起价 $1.788 Telegram - 浏览量 ~ 1 帖子 ~ 即时 | 每 1000 起价 $0.005 Telegram - 互动反应 | 每 1000 起价 $1.306 ㅤㅤTWITTER (x.com) 服务 Twitter (x.com) - 点赞 ~ 200k ~ 10k/天 ~ 亚洲 | 每 1000 起价 $1.187 Twitter (x.com) - 转推 ~ 200k ~ 10k/天 ~ 亚洲 | 每 1000 起价 $0.894 Twitter (x.com) - 粉丝 | 不掉粉 | 30天补充 | 每 1 小时自动补充 | 每 1000 起价 $19.50 Twitter (x.com) - 点赞 ~ 350k ~ 2k-10k/天 ~ 混合 ~ | 每 1000 起价 $1.727 Twitter (x.com) - 推文浏览量 ~ 最大 100M ~ 30天补充 ~ 10M/天 ~ 即时 | 每 1000 起价 $0.032 Twitter (x.com) - 推文展示次数 ~ 最大 1M ~ 1M/天 ~ 即时 | 每 1000 起价 $0.114 ㅤㅤTHREADS Threads - 点赞 ~ 不补充 ~ 1k-10k/天 ~ 0-1小时 | 每 1000 起价 $1.045 Threads - 粉丝 ~ 500-1k/天 ~ 不补充 ~ 即时 | 每 1000 起价 $2.438 Threads - 转发 ~ 1k-5k/天 ~ 30天补充 ~ 0-2小时 | 每 1000 起价 $8.125 ㅤㅤLIKEE Likee - 粉丝 ~ 最大 30k ~ 真实 ~ 即时 | 每 1000 起价 $10.112 Likee - 点赞 ~ 最大 30k ~ 真实 ~ 即时 | 每 1000 起价 $3.787 Likee - 分享 ~ 最大 30k ~ 真实 ~ 即时 | 每 1000 起价 $2.649 Likee - 评论 ~ 最大 30k ~ 真实 ~ 即时 | 每 1000 起价 $11.473 Likee - 浏览量 ~ 最大 1M ~ 真实 ~ 即时 | 每 1000 起价 $3.787 ㅤㅤTIKTOK 服务   TikTok - 浏览量 ~ 10k-100k/天 | 每 1000 起价 $0.033 TikTok - 点赞 + 浏览量 ~ 30天 ~ 3k-5k/天 | 每 1000 起价 $0.203 TikTok - 粉丝 ~ 30天 ~ 5k/天 | 每 1000 起价 $2.474 TikTok - 分享 ~ 30天 ~ 1M/天 | 每 1000 起价 $0.163 TikTok - 收藏 ~ 30天 ~ 3k-5k/天 | 每 1000 起价 $0.163 ㅤㅤFACEBOOK 服务 Facebook - 视频浏览量 ~ 5k-100k/天 | 每 1000 起价 $0.11 Facebook - 帖子点赞 ~ 5k/天 | 每 1000 起价 $2.548 Facebook - 页面点赞 + 粉丝 ~ 1k-10k/天 ~ 即时 | 每 1000 起价 $0.85 Facebook - 分享 ~ 30天 ~ 1k-5k/天 | 每 1000 起价 $1.451 Facebook - 帖子/照片点赞 ~ 20k ~ 永久 ~ 1k-50k/天 ~ 即时 | 每 1000 起价 $0.66 Facebook - 表情符号帖子点赞 ~ 1k-5k/天 ~ 即时 | 每 1000 起价 $0.943 Facebook 直播 [观看 15 分钟] | 每 1000 起价 $1.338 Facebook - 视频/Reels 浏览量 ~ 3秒 ~ 500k-1M/天 | 每 1000 起价 $0.11 ㅤㅤREDDIT 服务 Reddit 频道订阅者 [30天补充] [最大: 100M] [开始时间: 0-1小时] [速度: 5M/天] | 每 1000 起价 $2.08 Reddit 分享 | 500M | 永久补充 | 超快 | 0-10 分钟 | 100M/天 | 每 1000 起价 $0.371 Reddit 浏览量 | 500M | 永久补充 | 超快 | 0-10 分钟 | 100M/天 | 每 1000 起价 $0.371 Reddit 浏览量 + 分享 | 500M | 永久补充 | 超快 | 0-10 分钟 | 100M/天 | 每 1000 起价 $0.741 Reddit 链接分享 | 500M | 永久补充 | 超快 | 0-10 分钟 | 100M/天 | 每 1000 起价 $0.371 ㅤㅤDISCORD 服务 Discord 离线成员 | 阅读说明 | 添加机器人 | 高质量 | 最大 5K | 每 1000 起价 $1.58 Discord 全球在线服务器成员 | +2个月在线 | 最大 15K | 每 1000 起价 $108.986 Discord 服务器加速 | 1/3/6/12 个月 | 1x-14x 加速 | 每 1 次加速 起价 $2.23 Discord | 仅反应 | 超高质量 | 即时开始 | 每 1000 起价 $5.695 ㅤㅤLINKEDIN 服务 LinkedIn - 个人资料粉丝 ~ 最大 1k ~ 30天补充 ~ 500-1k/天 ~ 0-1小时 | 每 1000 起价 $21.97 LinkedIn - 页面粉丝 ~ 最大 1k ~ 30天补充 ~ 500-1k/天 ~ 0-1小时 | 每 1000 起价 $21.97 LinkedIn - 帖子点赞 [+ 展示次数] ~ 30天补充 ~ 500/天 ~ 即时 | 每 1000 起价 $16.90 LinkedIn - 评论 [随机] ~ 最大 1k ~ 不补充 ~ 30-50/天 ~ 1-6小时 | 每 1000 起价 $28.73 LinkedIn - 联系人连接 ~ 30天补充 ~ 500/天 ~ 即时 | 每 1000 起价 $28.73 ㅤㅤYOUTUBE 服务 YouTube - 浏览量 ~ 永久 ~ 500k-1M/天 | 每 1000 起价 $1.488 YouTube - 点赞 ~ 不补充 ~ 1k-5k/天 ~ 即时 | 每 1000 起价 $0.13 YouTube - 订阅者 | 中等质量 5K | 250-500/天 R30 | 每 1000 起价 $78.00 YouTube - 评论 ~ 随机 ~ 100k ~ 不补充 ~ 1k-5k/天 ~ 0-3小时 | 每 1000 起价 $19.013 YouTube - Shorts 点赞 ~ 50k-60k/天 ~ 30天补充 - 即时 | 每 1000 起价 $1.30 YouTube - 直播互动 ~ 帮助小型直播提升 ~ 即时 | 每 1000 起价 $0.192   ㅤㅤWHATSAPP 服务 WhatsApp 频道成员 [全球真实] [速度: 500/天] [开始: 0-1小时] | 每 1000 起价 $15.647 WhatsApp 群组成员 [混合国家] [真实质量] | 每 1000 起价 $30.893 WhatsApp 频道成员 [混合国家] [真实质量] | 每 1000 起价 $28.179 WhatsApp 频道表情反应 | 每 1000 起价 $6.797 WhatsApp 频道帖子表情反应 | 随机混合 | 每 1000 起价 $3.861 ㅤㅤTWITCH/KICK/TROVO 服务 Twitch - 粉丝 ~ 最大 1k ~ 1k/天 ~ 即时 | 每 1000 起价 $0.183 Twitch - 视频浏览量 ~ 最大 20k ~ 不补充 ~ 5k/天 ~ 0-1小时 | 每 1000 起价 $1.073 Twitch - 剪辑浏览量 ~ 最大 20k ~ 不补充 ~ 5k/天 ~ 0-1小时 | 每 1000 起价 $0.163 Twitch 直播浏览量 | 10 分钟 | 每 1000 起价 $0.597 Trovo 直播粉丝 [最大: 5K] [优惠] [1-2/小时] [30天补充] | 每 1000 起价 $18.688 Kick - 粉丝 ~ 1.2k ~ 不补充 ~ 1k/天 ~ 即时 | 每 1000 起价 $4.55 Kick - 直播观看者 - 稳定高质量观众 ~ 时长: 60 分钟 | 每 1000 起价 $40.30 ㅤㅤSOUNDCLOUD 服务   Soundcloud - 粉丝 ~ 2.5k ~ 30天补充 ~ 100-500/天 ~ 即时 | 每 1000 起价 $7.132 Soundcloud - 点赞 ~ 2.5k ~ 30天补充 ~ 100-500/天 ~ 即时 | 每 1000 起价 $7.132 Soundcloud - 转发 ~ 2.5k ~ 30天补充 ~ 100-500/天 ~ 即时 | 每 1000 起价 $7.132   ㅤㅤSPOTIFY 服务 Spotify - 粉丝 ~ 292k ~ 20k/天 | 每 1000 起价 $4.225 Spotify - 收藏 ~ 400k ~ 20k/天 | 每 1000 起价 $0.488 Spotify - 月度听众 ~ 永久 ~ 5k-20k/天 | 每 1000 起价 $1.788 ㅤㅤQUORA 服务 Quora.com - 赞 ~ 最大 10k ~ 30天补充 | 每 1000 起价 $8.125 Quora.com - 分享 ~ 最大 10k ~ 30天补充 | 每 1000 起价 $8.125 Quora.com - 粉丝 ~ 最大 10k ~ 30天补充 | 每 1000 起价 $8.45   ㅤㅤ网站流量 全球 / GEO 定向 - 自定义推荐流量 | 每 1000 次访问 起价 $1.284 全球来自 Google.com 流量 [自然] [自定义关键词] | 每 1000 次访问 起价 $0.244 全球来自 Google.com 流量 | 每 1000 次访问 起价 $0.244 全球来自 Facebook 流量 | 每 1000 次访问 起价 $0.244 全球来自 Instagram 流量 | 每 1000 次访问 起价 $0.244 全球来自 Quora 流量 | 每 1000 次访问 起价 $0.244 全球来自 Reddit 流量 | 每 1000 次访问 起价 $0.244 全球来自 YouTube 流量 | 每 1000 次访问 起价 $0.244 全球来自 Twitter 流量 | 每 1000 次访问 起价 $0.244 注意: 此文本块未展示我们全部产品;如需更多信息,请访问下方相关链接! 如果您有任何问题,我们的客服团队随时为您服务! 可用支付方式: 通过 PayPal、任意加密货币(+Binance Pay)、Telegram Stars、Cash App 或任意银行卡支付。 ⭐ 我们的社交媒体推广 SMM 面板 ⭐ SOCNET.PRO ⭐ 我们的在线商店 ⭐ SOCNET.STORE ⭐ Telegram 商店 ⭐ SOCNET.SHOP   新闻资源 Telegram 频道: https://t.me/socnet_news 联系方式与支持 Telegram 支持: https://t.me/socnet_support Discord 支持: socnet_support Discord 服务器: https://discord.gg/y9AStFFsrh WhatsApp 支持: https://wa.me/79051904467 WhatsApp 频道: https://whatsapp.com/channel/0029Vau0CMX002TGkD4uHa2n 电子邮件支持: solomonbog@socnet.store 我们已经运营了很长时间,积累了大量关于我们工作的评价! 我们的网站展示了大量积极和真实的用户评价!  获取 $1 首次试用奖励  只需在我们的网站提交主题为“获取试用奖励”的客服工单即可。 访问 SMM 面板 (可点击)或联系我们的 机器人 客服 ⭐ 我们邀请您 合作 并 赚钱 ⭐ 想在我们的商店销售您的产品或服务并赚取收益吗? 成为我们的合作伙伴或提出互利合作方案? 您可以通过本帖提供的 联系方式 与我们联系。    常见问题与退款政策   如有任何问题或疑问,我们的快速客服服务将立即回应您的请求! 若服务未完全符合要求或未达到承诺质量,只有在产品描述中注明了保修及诚信保修期限的情况下,才会提供退款。其他情况的服务退款将不被完全处理或发放!购买此类服务即表示您自动同意我们的未交付服务退款规则! 目前我们接受 CRYPTOMUS、Payeer、NotPayments、Perfect Money、俄罗斯和乌克兰银行卡、AliPay、BinancePay、CryptoBot、信用卡及 PayPal。 $1 注册奖励仅限于购买使用,并且只能在用户首次注册任何 SocNet 项目后使用一次。 我们重视每位客户,并通过联系方式为无效账号提供替换服务! p.s: 购买奖励可用于任意 SOCNET 项目:在线商店或 Telegram 机器人。
  • Topics

×
×
  • Create New...

AdBlock Extension Detected!

Our website is made possible by displaying online advertisements to our members.

Please disable AdBlock browser extension first, to be able to use our community.

I've Disabled AdBlock