Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

Welcome to our site

Take a moment to join our board

All Activity

This stream auto-updates     

  1. Today
  2. I was trying to change comet patch version to 5095, stumbled upon implementing Diffie-Hellman key exchange system, do i have to rewrite the socket accept to be task to be able to async send the key request? I'm trying to think of something will fit the best practices this source written in.
  3. Last week
  4. Smallxmac

    Board Closing - What is the future of Cooldown?

    Hello Everyone, Just to reiterate what @Spirited said today, thank you so much for being a part of Cooldown. This forum was created to give developers a good place to work with each other on a shared passion. However, I think Spirited covered that nicely and I completely feel the same way. I also wanted to point out that I will be stepping away from the community as a staff member. I enjoyed every second of working on this community with everyone. I will likely be around in the future, but not in a staff position. In one week we will put the forum in maintenance mode and start the backup. I might keep the forum in maintenance mode for a while just so people that link to the forum do not think it is just down. Thanks, Smallxmac
  5. Earlier
  6. Hi all, Thank you for being members of Cooldown. It was over two years ago now that we opened Cooldown to help those looking for a cleaner and more engaging game developer's board; but in creating something new, we never really found our footing. Cooldown has brilliant bursts of fun and interesting activity, but these days we socialize differently, support projects differently, and find help differently. This board currently has no drive or purpose, and finding that drive for everyone has been challenging. Therefore, until we can find a purpose to follow through on which requires a board, we have decided to close Cooldown. What this will mean is that the board will be backed up and the server will be turned off in one week. We have debated some regarding the future of Cooldown, and though I support running a board for those interested, I believe more discussion is required before making such commitments. Our current board owner Smallxmac will no longer be with the staff, but I hope you all can join me in thanking him wholeheartedly and in wishing him the best of luck in his future projects and opportunities. Thanks again, it's been a fun two and a half years. Feel free to follow up with me via private messages if you have questions. Best Regards, Spirited
  7. Spirited

    Packet Structure

    That's about how I got started as well. It just takes time and practice, like any other craft. About RC4, it was very popular back in the day. TQ's cipher is completely in-house, but reminiscent of RC4. There were a lot of variants of RC4 around that time since it was discovered to be cryptographically broken a year before Conquer was released. That's also probably why the password cipher is in RC5 as added security on top of their RC4 variant. (Of course, these are all just running theories of mine).
  8. Akhlis

    Packet Structure

    @Spirited Thanks for the help, I have been reading on RC4 for the past 2 hours and refreshing my knowledge on C# so I can better understand Comet's source. But at the same time, I have also taken a step back and I'm coming up with a systematic learning approach as up to this point I quickly read on things and trying stuff "until it sticks". Since I didn't really work with Networking stuff nor Encryption... I expect a good few days of trial and error before I can say "wth I'm looking at" And I missed to answer above, but you can bet I'm looking as making some Python contributions (for the cipher or package mappings/classes) once I get something solid that's proven to work. I have a few extra projects in mind as open source tutorials based on Comet (I love analytics and that's what I'm good at) but before this I'll need to remember pretty much everything on C#.
  9. Spirited

    Packet Structure

    Conquer Online packets aren't very large, especially in earlier patches. It's pretty standard to use a 16KB buffer in networking, but you can probably get away with half or a quarter of that. 1KB might be cutting it close. Regarding the nonsense, the entire packet is currently encrypted using TQ's custom cipher. You'll need to write your own implementation in Python to decrypt it so you can read the username string (which is why I posted mine as a reference since I know Go is a bit closer to Python). Here's a tutorial on encryption in Python: https://www.tutorialspoint.com/cryptography_with_python/cryptography_with_python_quick_guide.htm Here's an example socket system as well for Conquer Online: https://gitlab.com/spirited/comet/-/blob/master/src/Comet.Network/Sockets/TcpServerListener.cs#L105 You should be able to recognize some of the major calls even though it's in another language. C# also implements the Berkeley sockets API, just like Python.
  10. Akhlis

    Packet Structure

    Haha! 2 minute later and I think I progressed on the right track! This is the current Hex string that I got: '17840465d65315b5980f33a514cb756f5f89b022d1a6948cd2f90e54c7aef8b869f4011cd714d2f78ef3a39c40c17f6d5987ba20' And if I try to convert it to an ASCII string, I get nonsense, like this eÖSµ3¥Ëuo_°"ѦÒùTÇ®ø¸iô×Ò÷ó£@ÁmYº
  11. Akhlis

    Packet Structure

    @Spirited I have yet not implemented the XOR Cipher (reading on ways to implement it, but I can't lie that it feels a bit confusing right now). To your question "what code did you write to spit that out?" basic socket (TCP, Stream) nothing fancy as I only wanna see me be able to get the username from the packet (which tells me I got it down) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) s.listen() print("Server up and waiting for connection...") conn, addr = s.accept() with conn: print('Connected by', addr) while True: data = conn.recv(1024) # Do CO servers need a bigger buffersize? Python will return anything less, but will split if anything higher. if not data: break conn.sendall(data) # I debug on this line, so it will never execute. As for the HexDump I got this as an output: '17 84 04 65 D6 53 15 B5 98 0F 33 A5 14 CB 75 6F 5F 89 B0 22 D1 A6 94 8C D2 F9 0E 54 C7 AE F8 B8 69 F4 01 1C D7 14 D2 F7 8E F3 A3 9C 40 C1 7F 6D 59 87 BA 20' So my grapes with this is that at no time I know if I'm progressing or not, few times I ended up with nonsense text, but can't tell you if that was good either.
  12. Spirited

    Packet Structure

    Yeah, I remember a lot of students from my college started with Python and struggled a lot with data types as well once being introduced to C-syntax languages. Protocol design is very C-like though if you look at protobufs or binary based protocols like the one TQ developed for Conquer Online. It sounds like you're on the right track with understanding the binary header and body, but what you need to understand is how data types are stored in memory. For example, a 32-bit unsigned integer takes up 4 bytes (a byte being the smallest addressable unit in memory). When you look at a hex dump of an integer, you'll see 4 sets of 2 digit fields. The order of those fields are determined by byte ordering. In Little Endian, you read the bytes in backwards. So if you have 10 27 00 00, that's 0x2710 (which is 10,000 in decimal). Big Endian is used more in hashing algorithms and ciphers, and you read bytes forwards. So that same number would be 00 00 27 10 (a bit more straight forward). Network protocols like TQ's binary protocol use Little Endian because you can truncate fields in packing algorithms easier. Going to your packet hex dump, what code did you write to spit that out? If you want something a bit better that follows unix packet dump format, you can try this package: https://pypi.org/project/hexdump/ Post something from that and it'll be a lot easier to see what's going on. Finally, you should know that the Conquer Online client encrypts its packets. If you're not decrypting them on arrival to the server, then you're going to get a lot of nonsense. Here's the cipher used in 5017 written in C# (link). Optionally, I wrote the cipher as part of my project in Golang as an XOR cipherstream. You're totally welcome to use it and port it to Python (would be cool if you made a release here for it). Let me know if you have any other questions. Good luck! // Package tq implements TQ Digital Entertainment's XOR cipher using the `cipher.Stream` // interface from the `crypto` package. This cipher appears to be a stream in-house // variant of RC4, which is cryptographically broken and should not be used for secure // applications unless for the purposes of interoperability with legacy applications. package tq import ( "crypto/cipher" "io" ) var ( defaultSeed uint64 = 0x6d5c796213fa0f9d defaultKey *Key = NewKey(defaultSeed) ) // Stream is an instance of TQ's XOR stream cipher, initialized with a seeded key. Stream // implements Golang's `cipher.Stream` interface. Keys can be initialized separately from // the stream, and can be shared between multiple streams. type tqStream struct { bytes uint32 *Key } // New instantiates a new instance of `tqStream`. The key argument should be of type `Key`, // and may be a pointer to a shared key vector for all clients on login. If no key is // provided, the default will be used. func New(key *Key) cipher.Stream { if key == nil { key = defaultKey } stream := new(tqStream) stream.Key = key return stream } // NewStreamReader wraps a new cipher stream into an io.Reader. It calls `XORKeyStream` // to process each slice of data which passes through. Initializes the cipher with the // default key if none is specified. func NewStreamReader(input io.Reader, key *Key) *cipher.StreamReader { return &cipher.StreamReader { S: New(key), R: input } } // NewStreamWriter wraps a new cipher stream into an io.Writer. It calls `XORKeyStream` // to process each slice of data which passes through. If any Write call returns short // then the `StreamWriter` is out of sync and must be discarded. A `StreamWriter` has no // internal buffering; `Close` does not need to be called to flush write data. // Initializes the cipher with the default key if none is specified. func NewStreamWriter(output io.Writer, key *Key) *cipher.StreamWriter { return &cipher.StreamWriter { S: New(key), W: output } } // Reset the bytes counter to zero. func (stream *tqStream) Reset() { stream.bytes = 0 } // XORKeyStream sets dst to the result of XORing src with the key stream. Dst and src // must overlap entirely or not at all. func (stream *tqStream) XORKeyStream(dst, src []byte) { for i := 0; i < len(src); i++ { dst[i] = byte(src[i] ^ 0xAB) dst[i] = byte(dst[i] >> 4 | dst[i] << 4) dst[i] = byte(dst[i] ^ stream.low[stream.bytes & 0xFF]) dst[i] = byte(dst[i] ^ stream.high[stream.bytes >> 8]) stream.bytes++ } } package tq import ( "encoding/binary" ) // Key defines a cipher key structure which can be shared between multiple streams. The // key structure can be modified using a generator and new inputs from the game server; // however, this functionality was abandoned in favor of stronger cryptographic // algorithms in later client versions. type Key struct { low [0x100]byte high [0x100]byte } // NewKey returns a new Key structure with generated low and high vectors. The keys // can be generated using the default seed or any unsigned integer seed. func NewKey(seed uint64) *Key { key := new(Key) gen := make([]byte, 8) binary.LittleEndian.PutUint64(gen[:], seed) for i := 0; i < 0x100; i++ { key.low[i] = gen[0] key.high[i] = gen[4] gen[0] = byte((gen[1] + byte(gen[0] * gen[2])) * gen[0] + gen[3]) gen[4] = byte((gen[5] - byte(gen[4] * gen[6])) * gen[4] + gen[7]) } return key } // Generate was used in older versions of the Conquer Online client to generate new key // vectors after establishing a connection to the game server and receiving the first // packet. The packet contains the two key derivation variables: an account identifier // and an access token for the server. Returns a new key structure. func (key *Key) Generate(accountID, token uint32) *Key { result := new(Key) seed := uint32(((token + accountID) ^ 0x4321) ^ token) gen := make([]byte, 8) binary.LittleEndian.PutUint32(gen[0:], seed) binary.LittleEndian.PutUint32(gen[4:], uint32(seed * seed)) for i := 0; i < 0x100; i++ { result.low[i] = byte(key.low[i] ^ gen[i % 4]) result.high[i] = byte(key.high[i] ^ gen[4 + (i % 4)]) } return result }
  13. Akhlis

    Packet Structure

    Since my introduction, after work, I have been banging my head with reading the packets that I receive thru a 5017 client, and I have a few questions since I'm lost and I feel I'm moving in circles without a clear path: I don't know the order (and the names from wiki) of the packets being sent. I keep looking at the wiki, trying to figure out 2 things: Properly read the Header(s) and Figuring out which packet I have at hand. I think, I might have (maybe?) wrong bytes? Isn't a byte always formed of \xxx? Because at time I see weird stuff like \xd6S <- 4 chars after the slash. Or even worst \xcbuo_ ... whaaaat? Why I'm thinking so? When I try to calculate the size, I get "bad char in struct format". Is there anything I'm missing? (What is Native, big-endian , little-endian even means?!?! 😅 ) Another issue is trying to figure out a few things as I try to "transform" the DataTypes listed in the Wiki from C# to C++ to Python. (struct Python documentation assumes I'm working with C++ datatypes). And lastly, before I go crazy, I'll post a bytes-string that I get, immediately after I click the "Enter" button and I see the "Connecting to account server...Please wait a moment..." b'\x17\x84\x04e\xa6\xb3\x05$\x99\xf9DC\x14\xcbuo_\x89\xb0".\xeb\xee\xad\x8fus\xe9\xc7\xae\xf8\xb8i\xf4\x01\x1c\xd7\x14\xd2\xf7\x8e\xf3\xa3\[email protected]\xc1\x7fmY\x87\xba ' So... as you can see, I'm totally lost. I don't know what packets I should receive when I try to login, I don't know wth I should do, and the numbers I get when I try to decode the bytes all look trash, some examples would be really big ints (positive or negative) formed of 7 digits or more. Anyways, with me being pissed... I'm still quite hyped up about this, and I tell you... I jumped of happiness when the debugged showed me the first bytes-string! PS: I'm looking to contribute with example/formats (DataTypes) for Python aspirants, once I get some unpacking done.
  14. Spirited

    Private Servers: Getting Started

    Managing expectations This thread helps outline some of the expectations and guiding principles of programming and running a private server for Conquer Online. If you're here, then you're probably either interested in learning how to program using Conquer Online private servers or just want to run a server yourself. Do note, no server right now comes without bugs or problems, and so programming knowledge is highly recommended and eventually necessary. As much as some boards claim to have "fully fixed" projects, that is a scam that I urge you do not buy into. Downloading server projects and clients You can find and download projects for private servers here: The Conquer Online private server community is very old, and therefore most projects are undocumented and without setup guides. Feel free to create a guide and post it to this section if you feel compelled, or ask questions in a new thread. If the server you're downloading doesn't have a guide, then know that most servers require MySQL. Follow the directions in the guide above; most servers have a reference to the username and password login for the database somewhere in the code or a configuration file. All server projects have been written for a specific patch of the game client. When downloading a client, be sure to select the correct patch number which corresponds with your server project: Asking questions Due to the code being super old and this community being super new, we'll be playing some catch up for a while. In the meantime, it's important that you ask questions so we can fill in those gaps. Before asking a question though, check the wiki: If the development wiki can't help you out, then create a new thread here and be sure to include the following: Summary of the problem or issue you're experiencing What the expected behavior is and what the actual behavior you're experiencing is Screenshots and error logs showing the problem Detailed steps on how you reproduce the problem Including these details really helps get your questions answered properly and quickly. Learning how to program There are lots of resources online for learning the basics! I got started programming a decade ago on Conquer Online private servers by writing NPC dialog boxes and fixing small bugs, but I knew the basics before giving that a go (which I highly recommend). To get started with programming, I suggest following Microsoft's guide on C# (most of the sources here are in C#, but otherwise just google tutorials): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/ It helps to make your own example projects. One really good first project is to make a small text-based adventure game. Maybe ask the player for their name, and then randomize some of their stats and have them fight against a monster with its own stats. You don't have to do that for your first project, but it's very important that you apply the lessons you learn (like you would in math). Good luck, and have fun with it.
  15. Smallxmac

    print("Hello World")

    Glad to see you here, I have a very similar story as you do. I have always been a firm believer that learning by example or by a project is one of the best ways to go. Seems like you are learning a lot of the same stuff I have been using in my day to day job. It sure is a lot of fun.
  16. Spirited

    print("Hello World")

    Hey man, glad to see you're still around and improving so much. Hopefully we're all a little better than our past selves from a decade ago. Hahaha Regarding the wiki, you can clone the repo and contribute to it just like any other git repo. I personally use Visual Studio Code because I like the markdown previewer, but you can use whatever you want. Right now, you have to be assigned the project as a contributor (which I've only given to a select group of people right now). I don't think pull requests work on the wiki portion of GitLab (I'll have to ask about that). If you can forward me your GitLab username, that'd be great. That WoW emulator project looks pretty clean.
  17. Akhlis

    print("Hello World")

    Hello Cooldown! What a fresh breath of air this forum (and gitlab wiki) brings me! I'm Akhlis, but you can call me Danny (L1nk1n*P4rK on ePvP) and like most (maybe all?) members, I used to love to play Conquer! Back from the days when chit-chatting in Ape City (for low players) and BirdIsland (for high players) was the cool thing that all kids did Oh the memories and nostalgia! Oh how I was shaking when I got my first DB drop (in happened in Phoenix at Bandits, second map) Like other people, I hope a few, my connection with conquer goes something like this. Buckle-up you are in for quite the story, this story will have 2 major sections, CO era and Post-CO era. Conquer Online Era: Start playing the game. Get in-love with it. Skip... most of the classes in HighSchool, so I can farm more meteors and more Virtue Points (Archer, Trojan, 3 waters)! Got scammed for the first time. Rage quit the game. Got back to it, we all know CO was our mistress that we dreamed about and could never leave for good. Found about ePvP and quickly dreamed I'll run the best CO server, mind tho I had no: Server administration + hosting experience (First server I had, was hosted on my own PC and was UP while my old ones allowed me to keep the PC on, haha) SQL, is this excel? SELECT what? FROM where? WHEN when? The hell is this gibberish?!?! C Sharp? Truth be told, the C is not that sharp... if you zoom on the letter you can see the pixels! Quickly stolen/leeched/downloaded a source code from ePvP and claimed it as my own. First Source I worked with was LOTF. I know it had many flaws, but really... between us, I loved it! Yeah sure it had problems when you had more than 30 players online (that dreaded login server!) but it was, what I considered back then, top notch! I had no programming experience (let alone networking and threading knowledge) but I managed to do my own stuff with it! So looking back I consider it a major win! This is when something happened for the first time while in the CO private server scene, I felt like the scum of the earth. tanelipe (active dev. on the source-code I used) joined my server, and immediately asked me "Did you do this server?"... Ofc on my mind installing a DB and configuring the IP/Port + User/Pass for DB meant I did it, so my answer was "Yes". He called me on my bullshit and told me how... low I was for claiming (hole/part?) of his work. I think I logged out 5 minutes later, ashamed and pretty much sick in my stomach. Fast forward a few weeks (more like 2 months) I have apologized to him and we started to hangout on MSN. He started to teach me and show me the in's and out's of the source-code. He became somewhat of a mentor. I admired him! How one guy could know so much? Keep in mind, I was young and easy to impress, maybe his technical skills would not be so godlike to the much older me, but I'm fond of those memories. Little he knows, he is the reason right now I work in IT as a programmer (more details in the Post-CO section) Fast forward, maybe 2 more months, with tanelipe's help and with some guidance from ePvP I managed to get, what was considered at that time, one of the most feature complete servers. TL;DR: It was really bad, full of bugs and I was struggling with keeping it running for more than 3 hours without the login server going bye-bye. Cool trick I found out! A cronjob + command could automatically resurrect the login service for me WHO KNEW?!?! Highlight was 70-something players online, I tell you... I felt like the world was at my feet! Fast forward few more months (around 3 or 4). I lost interest in the server, I discovered Qonquer and no-life the heck out of it! (Meteor with socket, lol!) Qonquer shuts down and I was lost, I wanted to play CO, but retail service was too expensive for me (P2W) and decided to come back to CO private server dev scene. I got to meet amazing people like Korvacs, IcedEarth, _Emme_, InfamousNoone, CptSky, from which I tried learn as much as possible, leech everything they knew about this "land of wonders" called CO servers. Things start to go south. The binary server was leaked, and the scene mentality instantly shifted overnight from custom made emulators, to "how can we run and modify this?" but more importantly, something I personally was and still am really against, everyone and their mother was creating bots for Ninjas. What about that service that had headless-clients running on a SaaS? Yeah, I wasn't into it, even tho I heard the money was sweet! (I wouldn't have been able to contribute to the bot creation in any way, I was still pretty much a "wanna-be" or novice when comes to coding) So once again, I quit the scene. Post-Conquer Online Era: Annoyed on the direction the scene was moving, I left it and started to actually learn coding and stop pretending I'm a coder. I watched and read tutorials, I brought books, I found out about open-source and started to read their code, then I found my first true love... Python. Before you spit on me, let me tell you how it changed my life. If it wasn't for CO dev scene, I wouldn't have found Python, the language that currently pays my bills! I got the chance to work for Mozilla in DevOps team (heavily focused on infrastructure for all OS types, including Android and iOS) and helped them ship a few Firefox versions! Currently I'm leading, as Senior Python Developer, 2 teams (8x Python devs, 3x Angular devs and 3x QA peers) for one of the largest fuel (Diesel and all Petrol variants) corp. from US and you know what, I loooove every day at work! I don't have time to do anything, because I always have a meeting, task, work anniversary or "things" to deal with. There is always something new or some guy comes with "the next thing his product owner needs". Why am I here: As I learned more and more about coding, as I got into microservices, APIs, AWS lambdas and everything that has to do with automation, I always had in my mind on one thing... maybe now is the time to come back and actually put my ass to work and learn networking, threading and how to develop an emulator. Here you come in place, please help me to understand where to start. I'm the type of guy that likes to smash the head against a wall and... not once I dropped production DB "Fun" times, but I learned a lot! I tried to figure out where to start, but I'm missing some basic knowledge, like where to read on all the information, what the enums are (I know of the gitlab wiki, but not sure how to interact with them) and how to do basic stuff. My current goal is as follows: Do a very basic emulator in Python. I know, I know, Python is slow and it sucks for async/threading. But my goal is not to create the newest and shiniest emulator ever, my goal is to learn, and Python is the best medium for me as I know it pretty well (expect networking, threading, byte operations). So with this, I plead you... give me something to manage the following milestones: login, send message. Maybe extended goals could be jumping and or some entity-controller (spawn some Peasants!). If I could show you an example of what I'm trying to achieve, check this very basic WoW Emulator made in Python. Thank you for reading this wall of text, and I'm sooo glad I found this forum!
  18. Spirited

    Ratifying Section Rules

    Additionally, I plan on adding recommendations for what's expected from members in this section. It's pretty often that people are confused on how to get started, and I'd like to help clarify those expectations to help reduce spam. That said, if anyone wants to make tutorials for introductory subjects, that would help make the section more friendly to outside people. I plan on adding a few things as well when I get the time.
  19. Spirited

    Ratifying Section Rules

    Hi all, Similar to the last thread, I'd like to propose a new set of rules specifically for the Conquer Online section. Conquer Online private server development is notoriously spammed by bot requests and commission requests, so these rules aim to help reduce spam and keep the section on-topic. Just a reminder, commission requests, posting pirated software (TQ Binaries), and demands for completed code are already against the board rules, so they will not be mentioned here. Server advertising section has a separate set of rules as well. Feedback is very welcome if you think something is off or missing. No bot or client hack requests, this includes requests for client modifications No selling coding services, projects, or tools No posting or advertising compiled bot or hack executable files In addition, I'd like to make some recommendations for when posting questions: Include a short summary of the problem Include the behavior you were expecting vs. the actual behavior Include steps on how you reproduced the problem Include pictures and errors you encountered Thanks, Spirited
  20. Hashirama

    [Clue] HP Bar on old clients

    @Adrian i'm sure that server side has no matter one this issue , but i just want a clue wherever i can start searching thanks i'm gonna do this ♥ thanks in adv
  21. Adrian

    [Clue] HP Bar on old clients

    i never researched for this feature, but maybe here goes a tip for a start: hp bars appears in entity objects when their uid is between the right range. actually there's no data sent to client that tells the client the entity is a monster or a player, it's just the uid. using asm, you could find where that check for uid occurs and then remove the jmp for uids of entities that are not a monster. makes sense?
  22. Spirited

    [Clue] HP Bar on old clients

    Moved to the correct section.
  23. Spirited

    UI/UX Mocking with Adobe XD

    Yeah, I found with anything photography based or videography based... Adobe is still sadly untouched.
  24. Hashirama

    [Clue] HP Bar on old clients

    Regards , for a long time , i was hope to learn ASM basics then i did it i hope too others help me to create a HPBar client sided on Conquer.exe on old clients 5017 ~ 5165 i think client version number has not matter all things are possible with asm there is anyone here have any clue to change player object to monster object on conquer.exe ? 😖 thanks in advance😉
  25. Omicron

    UI/UX Mocking with Adobe XD

    They still have the 65% student discount though... At the moment, the competitors don't really scratch Adobe products much though.
  26. Spirited

    UI/UX Mocking with Adobe XD

    I mean, they did have student discounts and 30-day trials before. Now they just have subscriptions. It's not great for anyone like me who does photography as a hobby, or for the common independent artist who has to pay for it. Competitors are already stealing their customers though with a non-subscription model, so there's still hope. It's all about that product features to price ratio. What's horrible though is that you can still buy the non-subscription based Lightroom 6 from their website, but they ended support for it. So to get my camera working, I have to either pirate a hacked version of Lightroom CC or use their media importer and convert everything first. Adobe is such a horrible company.
  27. Omicron

    UI/UX Mocking with Adobe XD

    To be fair, this subscription model made it very easy for (beginning) professionals to use their products. Where as before you had to pay a few hundred bucks per product to start whereas you can start using it for like 20 bucks a month now (1 product). Funny part is though, the trade-off for being easier accessible is paying a lot more, fixed small fee a month vs big (2) yearly investment.
  1. Load more activity
×

Important Information

By using this site, you agree to our Terms of Use.